Skip to content

Commit

Permalink
Merge branch 'asf-update'
Browse files Browse the repository at this point in the history
  • Loading branch information
Citrinate committed Apr 2, 2024
2 parents ed0a93e + 1d3bb22 commit 1925a61
Show file tree
Hide file tree
Showing 15 changed files with 444 additions and 123 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -488,3 +488,6 @@ $RECYCLE.BIN/

# Windows shortcuts
*.lnk

# https://www.paraesthesia.com/archive/2022/09/30/strongly-typed-resources-with-net-core/
*.Designer.cs
2 changes: 1 addition & 1 deletion ArchiSteamFarm
Submodule ArchiSteamFarm updated 278 files
42 changes: 0 additions & 42 deletions FreePackages/AdapterBridge.cs

This file was deleted.

17 changes: 8 additions & 9 deletions FreePackages/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Linq;
using System.Text;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Localization;
using ArchiSteamFarm.Steam;

namespace FreePackages {
Expand Down Expand Up @@ -66,7 +65,7 @@ internal static class Commands {
}

if (!bot.IsConnectedAndLoggedOn) {
return FormatBotResponse(bot, Strings.BotNotConnected);
return FormatBotResponse(bot, ArchiSteamFarm.Localization.Strings.BotNotConnected);
}

if (!PackageHandler.Handlers.Keys.Contains(bot.BotName)) {
Expand All @@ -84,7 +83,7 @@ internal static class Commands {
HashSet<Bot>? bots = Bot.GetBots(botNames);

if ((bots == null) || (bots.Count == 0)) {
return access >= EAccess.Owner ? FormatStaticResponse(String.Format(Strings.BotNotFound, botNames)) : null;
return access >= EAccess.Owner ? FormatStaticResponse(String.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null;
}

IEnumerable<string?> results = bots.Select(bot => ResponseClearQueue(bot, ArchiSteamFarm.Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID)));
Expand All @@ -100,7 +99,7 @@ internal static class Commands {
}

if (!bot.IsConnectedAndLoggedOn) {
return FormatBotResponse(bot, Strings.BotNotConnected);
return FormatBotResponse(bot, ArchiSteamFarm.Localization.Strings.BotNotConnected);
}

if (!PackageHandler.Handlers.Keys.Contains(bot.BotName)) {
Expand All @@ -118,7 +117,7 @@ internal static class Commands {
HashSet<Bot>? bots = Bot.GetBots(botNames);

if ((bots == null) || (bots.Count == 0)) {
return access >= EAccess.Owner ? FormatStaticResponse(String.Format(Strings.BotNotFound, botNames)) : null;
return access >= EAccess.Owner ? FormatStaticResponse(String.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null;
}

IEnumerable<string?> results = bots.Select(bot => ResponseQueueStatus(bot, ArchiSteamFarm.Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID)));
Expand All @@ -134,7 +133,7 @@ internal static class Commands {
}

if (!bot.IsConnectedAndLoggedOn) {
return FormatBotResponse(bot, Strings.BotNotConnected);
return FormatBotResponse(bot, ArchiSteamFarm.Localization.Strings.BotNotConnected);
}

if (!PackageHandler.Handlers.Keys.Contains(bot.BotName)) {
Expand All @@ -154,7 +153,7 @@ internal static class Commands {

if ((index > 0) && (entry.Length > index + 1)) {
if (!uint.TryParse(entry[(index + 1)..], out gameID) || (gameID == 0)) {
response.AppendLine(FormatBotResponse(bot, string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(gameID))));
response.AppendLine(FormatBotResponse(bot, string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.ErrorIsInvalid, nameof(gameID))));

continue;
}
Expand All @@ -163,7 +162,7 @@ internal static class Commands {
} else if (uint.TryParse(entry, out gameID) && (gameID > 0)) {
type = "SUB";
} else {
response.AppendLine(FormatBotResponse(bot, string.Format(CultureInfo.CurrentCulture, Strings.ErrorIsInvalid, nameof(gameID))));
response.AppendLine(FormatBotResponse(bot, string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.ErrorIsInvalid, nameof(gameID))));

continue;
}
Expand All @@ -190,7 +189,7 @@ internal static class Commands {
HashSet<Bot>? bots = Bot.GetBots(botNames);

if ((bots == null) || (bots.Count == 0)) {
return access >= EAccess.Owner ? FormatStaticResponse(String.Format(Strings.BotNotFound, botNames)) : null;
return access >= EAccess.Owner ? FormatStaticResponse(String.Format(ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null;
}

IEnumerable<string?> results = bots.Select(bot => ResponseQueueLicense(bot, ArchiSteamFarm.Steam.Interaction.Commands.GetProxyAccess(bot, access, steamID), licenses, useFilter));
Expand Down
97 changes: 97 additions & 0 deletions FreePackages/Data/ASFInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Web.Responses;
using FreePackages.Localization;

// There are limitations to using PICS for discovery such that an account that's online 24/7 can still miss certain free games
// For more information see here: https://github.com/Citrinate/FreePackages/commit/7541807f10e8dde53b1352a2c103b867e5446fa1#commitcomment-137669223
// To fill in some of these gaps, we periodically check the free apps/subs list provided by https://github.com/C4illin/ASFinfo

namespace FreePackages {
internal static class ASFInfo {
private static Uri Source = new("https://gist.githubusercontent.com/C4illin/e8c5cf365d816f2640242bf01d8d3675/raw/Steam%2520Codes");
private static TimeSpan UpdateFrequency = TimeSpan.FromHours(1);

private static Timer UpdateTimer = new(async e => await DoUpdate().ConfigureAwait(false), null, Timeout.Infinite, Timeout.Infinite);

internal static void Update() {
UpdateTimer.Change(TimeSpan.FromMinutes(15), UpdateFrequency);
}

private static async Task DoUpdate() {
ArgumentNullException.ThrowIfNull(ASF.WebBrowser);
ArgumentNullException.ThrowIfNull(FreePackages.GlobalCache);

StreamResponse? response = await ASF.WebBrowser.UrlGetToStream(Source).ConfigureAwait(false);

if (response == null) {
ASF.ArchiLogger.LogNullError(response);

return;
}

if (response.Content == null) {
ASF.ArchiLogger.LogNullError(response.Content);

return;
}

HashSet<uint> appIDs = new();
HashSet<uint> packageIDs = new();
uint itemCount = 0;

using (StreamReader sr = new StreamReader(response.Content)) {
while (sr.Peek() >= 0) {
itemCount++;
string? line = sr.ReadLine();

if (line == null) {
ASF.ArchiLogger.LogNullError(line);

return;
}

if (itemCount <= FreePackages.GlobalCache.LastASFInfoItemCount) {
continue;
}

// Match examples: a/12345 or s/12345
Match item = Regex.Match(line, "(?<type>[as])/(?<id>[0-9]+)");

if (!item.Success) {
ASF.ArchiLogger.LogGenericError(String.Format("{0}: {1}", Strings.ASFInfoParseFailed, line));

return;
}

if (!uint.TryParse(item.Groups["id"].Value, out uint id)) {
ASF.ArchiLogger.LogGenericError(String.Format("{0}: {1}", Strings.ASFInfoParseFailed, line));

return;
}

if (item.Groups["type"].Value == "a") {
// App
appIDs.Add(id);
} else if (item.Groups["type"].Value == "s") {
// Sub
packageIDs.Add(id);
}
}
}

if (appIDs.Count == 0 && packageIDs.Count == 0) {
return;
}

PackageHandler.Handlers.Values.ToList().ForEach(x => x.BotCache.AddChanges(appIDs, packageIDs));
FreePackages.GlobalCache.UpdateASFInfoItemCount(itemCount);
}
}
}
3 changes: 1 addition & 2 deletions FreePackages/Data/BotCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;
using SteamKit2;

namespace FreePackages {
Expand Down Expand Up @@ -68,7 +67,7 @@ internal BotCache(string filePath) : this() {
string json = await File.ReadAllTextAsync(filePath).ConfigureAwait(false);

if (string.IsNullOrEmpty(json)) {
ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json)));
ASF.ArchiLogger.LogGenericError(string.Format(ArchiSteamFarm.Localization.Strings.ErrorIsEmpty, nameof(json)));

return null;
}
Expand Down
5 changes: 3 additions & 2 deletions FreePackages/Data/CardApps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Threading.Tasks;
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Web.Responses;
using FreePackages.Localization;

namespace FreePackages {
internal static class CardApps {
Expand All @@ -26,7 +27,7 @@ private static async Task DoUpdate() {
ObjectResponse<Badges>? response = await ASF.WebBrowser.UrlGetToJsonObject<Badges>(request).ConfigureAwait(false);

if (response == null) {
ASF.ArchiLogger.LogGenericDebug("Failed to fetch badge data for free packages");
ASF.ArchiLogger.LogGenericDebug(Strings.BadgeDataFetchFailed);
UpdateTimer.Change(TimeSpan.FromMinutes(1), UpdateFrequency);

return;
Expand All @@ -38,7 +39,7 @@ private static async Task DoUpdate() {
AppIDs = response.Content.Data.Keys.Select(uint.Parse).ToHashSet();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
ASF.ArchiLogger.LogGenericError("Failed to parse badge data for free packages");
ASF.ArchiLogger.LogGenericError(Strings.BadgeDataParsingFailed);

return;
}
Expand Down
14 changes: 11 additions & 3 deletions FreePackages/Data/GlobalCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
using ArchiSteamFarm.Core;
using ArchiSteamFarm.Helpers;
using ArchiSteamFarm.Helpers.Json;
using ArchiSteamFarm.Localization;

namespace FreePackages {
internal sealed class GlobalCache : SerializableFile {
private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, $"{nameof(FreePackages)}.cache");

[JsonInclude]
[JsonRequired]
internal uint LastChangeNumber { get; private set; }

[JsonInclude]
internal uint LastASFInfoItemCount { get; private set; }

public bool ShouldSerializeLastChangeNumber() => LastChangeNumber > 0;
public bool ShouldSerializeLastASFInfoItemCount() => LastASFInfoItemCount > 0;

[JsonConstructor]
internal GlobalCache() {
Expand All @@ -34,7 +36,7 @@ internal GlobalCache() {
string json = await File.ReadAllTextAsync(SharedFilePath).ConfigureAwait(false);

if (string.IsNullOrEmpty(json)) {
ASF.ArchiLogger.LogGenericError(string.Format(Strings.ErrorIsEmpty, nameof(json)));
ASF.ArchiLogger.LogGenericError(string.Format(ArchiSteamFarm.Localization.Strings.ErrorIsEmpty, nameof(json)));

return null;
}
Expand All @@ -60,5 +62,11 @@ internal void UpdateChangeNumber(uint currentChangeNumber) {

Utilities.InBackground(Save);
}

internal void UpdateASFInfoItemCount(uint currentASFInfoItemCount) {
LastASFInfoItemCount = currentASFInfoItemCount;

Utilities.InBackground(Save);
}
}
}
13 changes: 3 additions & 10 deletions FreePackages/FreePackages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,18 @@
using SteamKit2;
using System.Text.Json;
using ArchiSteamFarm.Helpers.Json;
using System.Reflection;

namespace FreePackages {
[Export(typeof(IPlugin))]
public sealed class FreePackages : IASF, IBotModules, ISteamPICSChanges, IBotSteamClient, IBotConnection, IBotCommand2 {
public sealed class FreePackages : IASF, IBotModules, ISteamPICSChanges, IBotSteamClient, IBotConnection, IBotCommand2, IGitHubPluginUpdates {
public string Name => nameof(FreePackages);
public string RepositoryName => "Citrinate/FreePackages";
public Version Version => typeof(FreePackages).Assembly.GetName().Version ?? new Version("0");
internal static GlobalCache? GlobalCache;

public Task OnLoaded() {
ASF.ArchiLogger.LogGenericInfo("Free Packages ASF Plugin by Citrinate");

// ASFEnhanced Adapter https://github.com/chr233/ASFEnhanceAdapterDemoPlugin
var flag = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
var handler = typeof(AdapterBridge).GetMethod(nameof(AdapterBridge.Response), flag);
const string pluginId = nameof(FreePackages);
const string cmdPrefix = "FREEPACKAGES";
const string repoName = "Citrinate/FreePackages";
AdapterBridge.InitAdapter(Name, pluginId, cmdPrefix, repoName, handler);

return Task.CompletedTask;
}

Expand All @@ -41,6 +33,7 @@ public async Task OnASFInit(IReadOnlyDictionary<string, JsonElement>? additional
}

CardApps.Update();
ASFInfo.Update();
}

public async Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
Expand Down
15 changes: 14 additions & 1 deletion FreePackages/FreePackages.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

<PropertyGroup>
<Authors>Citrinate</Authors>
<AssemblyVersion>1.4.4</AssemblyVersion>
<AssemblyVersion>1.4.5.0</AssemblyVersion>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<TargetFramework>net8.0</TargetFramework>
<Deterministic>true</Deterministic>
<CoreCompileDependsOn>PrepareResources;$(CompileDependsOn)</CoreCompileDependsOn>
</PropertyGroup>

<ItemGroup>
Expand All @@ -21,4 +22,16 @@
<InternalsVisibleTo Include="FreePackages.Tests" />
</ItemGroup>

<!-- https://www.paraesthesia.com/archive/2022/09/30/strongly-typed-resources-with-net-core/ -->
<ItemGroup>
<EmbeddedResource Update="Localization\Strings.resx">
<Generator>MSBuild:Compile</Generator>
<LastGenOutput>Strings.Designer.cs</LastGenOutput>
<StronglyTypedFileName>$(IntermediateOutputPath)\Strings.Designer.cs</StronglyTypedFileName>
<StronglyTypedLanguage>CSharp</StronglyTypedLanguage>
<StronglyTypedNamespace>FreePackages.Localization</StronglyTypedNamespace>
<StronglyTypedClassName>Strings</StronglyTypedClassName>
</EmbeddedResource>
</ItemGroup>

</Project>
Loading

0 comments on commit 1925a61

Please sign in to comment.