Skip to content

Commit

Permalink
Merge pull request #65 from maxisoft/dev
Browse files Browse the repository at this point in the history
Comprehensive Update and Fixes for ASFFreeGames Plugin v1.5.1
  • Loading branch information
maxisoft authored May 5, 2024
2 parents f9d82b3 + 45e9733 commit 928784c
Show file tree
Hide file tree
Showing 26 changed files with 299 additions and 190 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on: [push, pull_request]
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
DOTNET_SDK_VERSION: 7.0.x
DOTNET_FRAMEWORK: net7.0
DOTNET_SDK_VERSION: 8.0.x
DOTNET_FRAMEWORK: net8.0

jobs:
main:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ env:
CONFIGURATION: Release
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
DOTNET_SDK_VERSION: 7.0.x
NET_CORE_VERSION: net7.0
DOTNET_SDK_VERSION: 8.0.x
NET_CORE_VERSION: net8.0
NET_FRAMEWORK_VERSION: net48
PLUGIN_NAME: ASFFreeGames

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
DOTNET_SDK_VERSION: 7.0.x
DOTNET_SDK_VERSION: 8.0.x

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
Expand Down
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: 29 additions & 24 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,20 @@ 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")!;
#pragma warning disable CA2007
await using Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.ASFinfo.json")!;
#pragma warning restore CA2007
JsonNode jsonNode = await JsonNode.ParseAsync(stream).ConfigureAwait(false) ?? JsonNode.Parse("{}")!;

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

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

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
23 changes: 14 additions & 9 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 @@ -54,20 +54,14 @@ internal static PluginContext Context {
public ASFFreeGamesOptions Options => OptionsField;
private ASFFreeGamesOptions OptionsField = new();

private readonly ICollectIntervalManager CollectIntervalManager;
private readonly CollectIntervalManager CollectIntervalManager;

public ASFFreeGamesPlugin() {
CommandDispatcher = new CommandDispatcher(Options);
CollectIntervalManager = new CollectIntervalManager(this);
_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,16 @@ 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") ?? "";
#pragma warning disable CAC001
#pragma warning disable CA2007
await using StreamResponse? result = await web.UrlGetToStream(new Uri(GetIPAddressUrl), cancellationToken: cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2007
#pragma warning restore CAC001

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 { }
Loading

0 comments on commit 928784c

Please sign in to comment.