Skip to content

Commit

Permalink
Upgrade plugin to align with latest ASF version
Browse files Browse the repository at this point in the history
- Upgraded target framework to .NET 8.0 to match ASF requirements.
- Updated ASF submodule to the latest stable release.
- Transitioned from Newtonsoft.Json to System.Text.Json for improved compatibility with ASF.
- Refined GaussianRandom implementation to function with the latest trimmed ASF binary.
- Enhanced RedditHelper to utilize System.Text.Json, improving JSON handling.
- Modified GaussianRandom to utilize a more reliable RNG method compatible with ASF's trimmed version.
- Various improvements and code cleanups in line with ASF's updated codebase.
  • Loading branch information
maxisoft committed May 4, 2024
1 parent e765d8d commit e4fc3ae
Show file tree
Hide file tree
Showing 23 changed files with 235 additions and 137 deletions.
2 changes: 2 additions & 0 deletions ASFFreeGames.Tests/ASFFreeGames.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>

<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion ASFFreeGames.Tests/RandomUtilsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public static TheoryData<double, double, int, double> GetTestData() =>
[SuppressMessage("ReSharper", "InconsistentNaming")]
public void NextGaussian_Should_Have_Expected_Mean_And_Std(double mean, double standardDeviation, int sampleSize, double marginOfError) {
// Arrange
using RandomUtils.GaussianRandom rng = new();
RandomUtils.GaussianRandom rng = new();

// Act
// Generate a large number of samples from the normal distribution
Expand Down
53 changes: 28 additions & 25 deletions ASFFreeGames.Tests/Reddit/RedditHelperTests.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
using Maxisoft.ASF.Reddit;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Maxisoft.Utils.Collections.Spans;
using Xunit;

namespace ASFFreeGames.Tests.Reddit;
namespace Maxisoft.ASF.Tests.Reddit;

public sealed class RedditHelperTests {
private static readonly Lazy<JToken> ASFinfo = new(LoadAsfinfoJson);

[Fact]
public void TestNotEmpty() {
JToken payload = ASFinfo.Value;
RedditGameEntry[] entries = RedditHelper.LoadMessages(payload.Value<JObject>("data")!["children"]!);
public async Task TestNotEmpty() {
RedditGameEntry[] entries = await LoadAsfinfoEntries().ConfigureAwait(false);
Assert.NotEmpty(entries);
}

[Theory]
[InlineData("s/762440")]
[InlineData("a/1601550")]
public void TestContains(string appid) {
JToken payload = ASFinfo.Value;
RedditGameEntry[] entries = RedditHelper.LoadMessages(payload.Value<JObject>("data")!["children"]!);
public async Task TestContains(string appid) {
RedditGameEntry[] entries = await LoadAsfinfoEntries().ConfigureAwait(false);
Assert.Contains(new RedditGameEntry(appid, default(ERedditGameEntryKind), long.MaxValue), entries, new GameEntryIdentifierEqualityComparer());
}

[Fact]
public void TestMaintainOrder() {
JToken payload = ASFinfo.Value;
RedditGameEntry[] entries = RedditHelper.LoadMessages(payload.Value<JObject>("data")!["children"]!);
public async Task TestMaintainOrder() {
RedditGameEntry[] entries = await LoadAsfinfoEntries().ConfigureAwait(false);
int app762440 = Array.FindIndex(entries, static entry => entry.Identifier == "s/762440");
int app1601550 = Array.FindIndex(entries, static entry => entry.Identifier == "a/1601550");
Assert.InRange(app762440, 0, long.MaxValue);
Expand All @@ -42,9 +43,8 @@ public void TestMaintainOrder() {
}

[Fact]
public void TestFreeToPlayParsing() {
JToken payload = ASFinfo.Value;
RedditGameEntry[] entries = RedditHelper.LoadMessages(payload.Value<JObject>("data")!["children"]!);
public async Task TestFreeToPlayParsing() {
RedditGameEntry[] entries = await LoadAsfinfoEntries().ConfigureAwait(false);
RedditGameEntry f2pEntry = Array.Find(entries, static entry => entry.Identifier == "a/1631250");
Assert.True(f2pEntry.IsFreeToPlay);

Expand All @@ -70,9 +70,8 @@ public void TestFreeToPlayParsing() {
}

[Fact]
public void TestDlcParsing() {
JToken payload = ASFinfo.Value;
RedditGameEntry[] entries = RedditHelper.LoadMessages(payload.Value<JObject>("data")!["children"]!);
public async Task TestDlcParsing() {
RedditGameEntry[] entries = await LoadAsfinfoEntries().ConfigureAwait(false);
RedditGameEntry f2pEntry = Array.Find(entries, static entry => entry.Identifier == "a/1631250");
Assert.False(f2pEntry.IsForDlc);

Expand All @@ -97,14 +96,18 @@ public void TestDlcParsing() {
Assert.False(paidEntry.IsForDlc);
}

private static JToken LoadAsfinfoJson() {
private static async Task<RedditGameEntry[]> LoadAsfinfoEntries() {
Assembly assembly = Assembly.GetExecutingAssembly();

using Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.ASFinfo.json")!;
await using Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.ASFinfo.json")!;
JsonNode jsonNode = await JsonNode.ParseAsync(stream).ConfigureAwait(false) ?? JsonNode.Parse("{}")!;

return RedditHelper.LoadMessages(jsonNode["data"]?["children"]!);
}

using StreamReader reader = new(stream);
using JsonTextReader jsonTextReader = new(reader);
private static async Task<string> ReadToEndAsync(Stream stream, CancellationToken cancellationToken) {
using StreamReader reader = new StreamReader(stream);

return JToken.Load(jsonTextReader);
return await reader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
}
}
5 changes: 5 additions & 0 deletions ASFFreeGames.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,10 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=TypeParameters/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="I" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=2c62818f_002D621b_002D4425_002Dadc9_002D78611099bfcb/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Type parameters"&gt;&lt;ElementKinds&gt;&lt;Kind Name="TYPE_PARAMETER" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=f9fce829_002De6f4_002D4cb2_002D80f1_002D5497c44f51df/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CustomTools/CustomToolsData/@EntryValue"></s:String>
<s:Boolean x:Key="/Default/Environment/Highlighting/HighlightingEnabled/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/Environment/Highlighting/HighlightingMode/@EntryValue">OnlyMarkers</s:String>
Expand Down Expand Up @@ -761,6 +765,7 @@
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EFormat_002ESettingsUpgrade_002EAlignmentTabFillStyleMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsParsFormattingSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002EJavaScript_002ECodeStyle_002ESettingsUpgrade_002EJsWrapperSettingsUpgrader/@EntryIndexedValue">True</s:Boolean>
Expand Down
2 changes: 1 addition & 1 deletion ASFFreeGames/ASFFreeGames.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<DebugSymbols>True</DebugSymbols>
<DebugType>pdbonly</DebugType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="ConfigureAwaitChecker.Analyzer" PrivateAssets="all" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="all" />
<PackageReference Include="NLog" />
<PackageReference Include="System.Composition.AttributedModel" IncludeAssets="compile" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="SteamKit2" />
</ItemGroup>

Expand Down
21 changes: 13 additions & 8 deletions ASFFreeGames/ASFFreeGamesPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Collections;
using ArchiSteamFarm.Plugins.Interfaces;
using ArchiSteamFarm.Steam;
using ASFFreeGames.Commands;
using ASFFreeGames.Configurations;
using JetBrains.Annotations;
using Maxisoft.ASF.Configurations;
using Newtonsoft.Json.Linq;
using SteamKit2;
using static ArchiSteamFarm.Core.ASF;

Expand All @@ -24,7 +25,6 @@ internal interface IASFFreeGamesPlugin {
}

#pragma warning disable CA1812 // ASF uses this class during runtime
[UsedImplicitly]
[SuppressMessage("Design", "CA1001:Disposable fields")]
internal sealed class ASFFreeGamesPlugin : IASF, IBot, IBotConnection, IBotCommand2, IUpdateAware, IASFFreeGamesPlugin {
internal const string StaticName = nameof(ASFFreeGamesPlugin);
Expand Down Expand Up @@ -62,12 +62,6 @@ public ASFFreeGamesPlugin() {
_context.Value = new PluginContext(Bots, BotContextRegistry, Options, LoggerFilter) { CancellationTokenLazy = new Lazy<CancellationToken>(() => CancellationTokenSourceLazy.Value.Token) };
}

public async Task OnASFInit(IReadOnlyDictionary<string, JToken>? additionalConfigProperties = null) {
ASFFreeGamesOptionsLoader.Bind(ref OptionsField);
Options.VerboseLog ??= GlobalDatabase?.LoadFromJsonStorage($"{Name}.Verbose")?.ToObject<bool?>() ?? Options.VerboseLog;
await SaveOptions(CancellationToken).ConfigureAwait(false);
}

public async Task<string?> OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
if (!Context.Valid) {
CreateContext();
Expand All @@ -92,6 +86,17 @@ public Task OnLoaded() {
return Task.CompletedTask;
}

public async Task OnASFInit(IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
ASFFreeGamesOptionsLoader.Bind(ref OptionsField);
JsonElement? jsonElement = GlobalDatabase?.LoadFromJsonStorage($"{Name}.Verbose");

if (jsonElement?.ValueKind is JsonValueKind.True) {
Options.VerboseLog = true;
}

await SaveOptions(CancellationToken).ConfigureAwait(false);
}

public async Task OnUpdateFinished(Version currentVersion, Version newVersion) => await SaveOptions(Context.CancellationToken).ConfigureAwait(false);

public Task OnUpdateProceeding(Version currentVersion, Version newVersion) => Task.CompletedTask;
Expand Down
4 changes: 2 additions & 2 deletions ASFFreeGames/BloomFilters/StringBloomFilterSpan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public StringBloomFilterSpan(BitSpan bitSpan, int k = 1) {
/// Adds a new item to the filter. It cannot be removed.
/// </summary>
/// <param name="item">The item.</param>
public void Add([JetBrains.Annotations.NotNull] in string item) {
public void Add(in string item) {
// start flipping bits for each hash of item
#pragma warning disable CA1062
int primaryHash = item.GetHashCode(StringComparison.Ordinal);
Expand All @@ -61,7 +61,7 @@ public void Add([JetBrains.Annotations.NotNull] in string item) {
/// </summary>
/// <param name="item"> The item. </param>
/// <returns> The <see cref="bool" />. </returns>
public bool Contains([JetBrains.Annotations.NotNull] in string item) {
public bool Contains(in string item) {
#pragma warning disable CA1062
int primaryHash = item.GetHashCode(StringComparison.Ordinal);
#pragma warning restore CA1062
Expand Down
2 changes: 2 additions & 0 deletions ASFFreeGames/Commands/CommandDispatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Steam;
using ASFFreeGames.Commands.GetIp;
using ASFFreeGames.Configurations;
using Maxisoft.ASF;

namespace ASFFreeGames.Commands {
Expand Down
3 changes: 2 additions & 1 deletion ASFFreeGames/Commands/FreeGamesCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Steam;
using ASFFreeGames.Configurations;
using Maxisoft.ASF;
using Maxisoft.ASF.Configurations;
using Maxisoft.ASF.Reddit;
Expand Down Expand Up @@ -190,7 +191,7 @@ private async Task<int> CollectGames(IEnumerable<Bot> bots, ECollectGameRequestS
int res = 0;

try {
ICollection<RedditGameEntry> games = await RedditHelper.GetGames().ConfigureAwait(false);
ICollection<RedditGameEntry> games = await RedditHelper.GetGames(cancellationToken).ConfigureAwait(false);

LogNewGameCount(games, VerboseLog || requestSource is ECollectGameRequestSource.RequestedByUser);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
using System;
using System.Globalization;
using System.IO;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;
using ArchiSteamFarm.Web;
using ArchiSteamFarm.Web.Responses;
using Maxisoft.ASF;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace ASFFreeGames.Commands;
namespace ASFFreeGames.Commands.GetIp;

// ReSharper disable once ClassNeverInstantiated.Local
internal sealed class GetIPCommand : IBotCommand {
private const string GetIPAddressUrl = "https://httpbin.org/ip";

Expand All @@ -28,8 +28,12 @@ internal sealed class GetIPCommand : IBotCommand {
}

try {
ObjectResponse<JToken>? result = await web.UrlGetToJsonObject<JToken>(new Uri(GetIPAddressUrl)).ConfigureAwait(false);
string origin = result?.Content?.Value<string>("origin") ?? "";
await using StreamResponse? result = await web.UrlGetToStream(new Uri(GetIPAddressUrl), cancellationToken: cancellationToken).ConfigureAwait(false);

if (result?.Content is null) { return null; }

GetIpReponse? reponse = await JsonSerializer.DeserializeAsync<GetIpReponse>(result.Content, cancellationToken: cancellationToken).ConfigureAwait(false);
string? origin = reponse?.Origin;

if (!string.IsNullOrWhiteSpace(origin)) {
return IBotCommand.FormatBotResponse(bot, origin);
Expand Down
3 changes: 3 additions & 0 deletions ASFFreeGames/Commands/GetIp/GetIpReponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace ASFFreeGames.Commands.GetIp;

internal record GetIpReponse(string Origin) { }
7 changes: 7 additions & 0 deletions ASFFreeGames/Commands/GetIp/GetIpReponseContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using System.Text.Json.Serialization;

namespace ASFFreeGames.Commands.GetIp;

//[JsonSourceGenerationOptions(WriteIndented = false, UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip)]
//[JsonSerializable(typeof(GetIpReponse))]
//internal partial class GetIpReponseContext : JsonSerializerContext { }
56 changes: 29 additions & 27 deletions ASFFreeGames/Configurations/ASFFreeGamesOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,46 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.Json.Serialization;
using ArchiSteamFarm.Steam;
using Maxisoft.ASF;
using Newtonsoft.Json;

namespace Maxisoft.ASF {
public class ASFFreeGamesOptions {
// Use TimeSpan instead of long for representing time intervals
[JsonProperty("recheckInterval")]
public TimeSpan RecheckInterval { get; set; } = TimeSpan.FromMinutes(30);
namespace ASFFreeGames.Configurations;

// Use Nullable<T> instead of bool? for nullable value types
[JsonProperty("randomizeRecheckInterval")]
public Nullable<bool> RandomizeRecheckInterval { get; set; }
public class ASFFreeGamesOptions {
// Use TimeSpan instead of long for representing time intervals
[JsonPropertyName("recheckInterval")]
public TimeSpan RecheckInterval { get; set; } = TimeSpan.FromMinutes(30);

[JsonProperty("skipFreeToPlay")]
public Nullable<bool> SkipFreeToPlay { get; set; }
// Use Nullable<T> instead of bool? for nullable value types
[JsonPropertyName("randomizeRecheckInterval")]
public bool? RandomizeRecheckInterval { get; set; }

// ReSharper disable once InconsistentNaming
[JsonProperty("skipDLC")]
public Nullable<bool> SkipDLC { get; set; }
[JsonPropertyName("skipFreeToPlay")]
public bool? SkipFreeToPlay { get; set; }

// Use IReadOnlyCollection<string> instead of HashSet<string> for blacklist property
[JsonProperty("blacklist")]
public IReadOnlyCollection<string> Blacklist { get; set; } = new HashSet<string>();
// ReSharper disable once InconsistentNaming
[JsonPropertyName("skipDLC")]
public bool? SkipDLC { get; set; }

[JsonProperty("verboseLog")]
public Nullable<bool> VerboseLog { get; set; }
// Use IReadOnlyCollection<string> instead of HashSet<string> for blacklist property
[JsonPropertyName("blacklist")]
public IReadOnlyCollection<string> Blacklist { get; set; } = new HashSet<string>();

#region IsBlacklisted
public bool IsBlacklisted(in GameIdentifier gid) {
if (Blacklist.Count <= 0) {
return false;
}
[JsonPropertyName("verboseLog")]
public bool? VerboseLog { get; set; }

return Blacklist.Contains(gid.ToString()) || Blacklist.Contains(gid.Id.ToString(NumberFormatInfo.InvariantInfo));
#region IsBlacklisted
public bool IsBlacklisted(in GameIdentifier gid) {
if (Blacklist.Count <= 0) {
return false;
}

public bool IsBlacklisted(in Bot? bot) => bot is null || ((Blacklist.Count > 0) && Blacklist.Contains($"bot/{bot.BotName}"));
#endregion
return Blacklist.Contains(gid.ToString()) || Blacklist.Contains(gid.Id.ToString(CultureInfo.InvariantCulture));
}

public bool IsBlacklisted(in Bot? bot) => bot is null || ((Blacklist.Count > 0) && Blacklist.Contains($"bot/{bot.BotName}"));
#endregion
}


8 changes: 8 additions & 0 deletions ASFFreeGames/Configurations/ASFFreeGamesOptionsContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using System.Text.Json.Serialization;
using ASFFreeGames.Configurations;

namespace Maxisoft.ASF.Configurations;

//[JsonSourceGenerationOptions(WriteIndented = false, UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
//[JsonSerializable(typeof(ASFFreeGamesOptions))]
//internal partial class ASFFreeGamesOptionsContext : JsonSerializerContext { }
Loading

0 comments on commit e4fc3ae

Please sign in to comment.