mirror of
https://github.com/JustArchiNET/ArchiSteamFarm
synced 2024-11-10 07:04:27 +00:00
Make STD work also in on-demand mode
This commit is contained in:
parent
8d1d508fe5
commit
da2fd37aa1
2 changed files with 109 additions and 81 deletions
|
@ -89,15 +89,15 @@ internal sealed class GlobalCache : SerializableFile {
|
|||
|
||||
internal ulong GetAppToken(uint appID) => AppTokens[appID];
|
||||
|
||||
internal Dictionary<uint, ulong> GetAppTokensForSubmission() => AppTokens.Where(appToken => (SteamTokenDumperPlugin.Config?.SecretAppIDs.Contains(appToken.Key) == false) && (appToken.Value > 0) && (!SubmittedApps.TryGetValue(appToken.Key, out ulong token) || (appToken.Value != token))).ToDictionary(static appToken => appToken.Key, static appToken => appToken.Value);
|
||||
internal Dictionary<uint, string> GetDepotKeysForSubmission() => DepotKeys.Where(depotKey => (SteamTokenDumperPlugin.Config?.SecretDepotIDs.Contains(depotKey.Key) == false) && !string.IsNullOrEmpty(depotKey.Value) && (!SubmittedDepots.TryGetValue(depotKey.Key, out string? key) || (depotKey.Value != key))).ToDictionary(static depotKey => depotKey.Key, static depotKey => depotKey.Value);
|
||||
internal Dictionary<uint, ulong> 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);
|
||||
internal Dictionary<uint, string> GetDepotKeysForSubmission() => DepotKeys.Where(depotKey => (SteamTokenDumperPlugin.Config?.SecretDepotIDs.Contains(depotKey.Key) != true) && !string.IsNullOrEmpty(depotKey.Value) && (!SubmittedDepots.TryGetValue(depotKey.Key, out string? key) || (depotKey.Value != key))).ToDictionary(static depotKey => depotKey.Key, static depotKey => depotKey.Value);
|
||||
|
||||
internal Dictionary<uint, ulong> GetPackageTokensForSubmission() {
|
||||
if (ASF.GlobalDatabase == null) {
|
||||
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
|
||||
}
|
||||
|
||||
return ASF.GlobalDatabase.PackageAccessTokensReadOnly.Where(packageToken => (SteamTokenDumperPlugin.Config?.SecretPackageIDs.Contains(packageToken.Key) == false) && (packageToken.Value > 0) && (!SubmittedPackages.TryGetValue(packageToken.Key, out ulong token) || (packageToken.Value != token))).ToDictionary(static packageToken => packageToken.Key, static packageToken => packageToken.Value);
|
||||
return ASF.GlobalDatabase.PackageAccessTokensReadOnly.Where(packageToken => (SteamTokenDumperPlugin.Config?.SecretPackageIDs.Contains(packageToken.Key) != true) && (packageToken.Value > 0) && (!SubmittedPackages.TryGetValue(packageToken.Key, out ulong token) || (packageToken.Value != token))).ToDictionary(static packageToken => packageToken.Key, static packageToken => packageToken.Value);
|
||||
}
|
||||
|
||||
internal static async Task<GlobalCache?> Load() {
|
||||
|
|
|
@ -37,6 +37,7 @@ using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Localization;
|
|||
using ArchiSteamFarm.Plugins;
|
||||
using ArchiSteamFarm.Plugins.Interfaces;
|
||||
using ArchiSteamFarm.Steam;
|
||||
using ArchiSteamFarm.Steam.Interaction;
|
||||
using ArchiSteamFarm.Storage;
|
||||
using ArchiSteamFarm.Web;
|
||||
using ArchiSteamFarm.Web.Responses;
|
||||
|
@ -67,7 +68,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
[JsonProperty]
|
||||
public override Version Version => typeof(SteamTokenDumperPlugin).Assembly.GetName().Version ?? throw new InvalidOperationException(nameof(Version));
|
||||
|
||||
public Task<uint> GetPreferredChangeNumberToStartFrom() => Task.FromResult(Config?.Enabled == true ? GlobalCache?.LastChangeNumber ?? 0 : 0);
|
||||
public Task<uint> GetPreferredChangeNumberToStartFrom() => Task.FromResult(GlobalCache?.LastChangeNumber ?? 0);
|
||||
|
||||
public async Task OnASFInit(IReadOnlyDictionary<string, JToken>? additionalConfigProperties = null) {
|
||||
if (!SharedInfo.HasValidToken) {
|
||||
|
@ -101,6 +102,24 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
}
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
GlobalCache? globalCache = await GlobalCache.Load().ConfigureAwait(false);
|
||||
|
||||
if (globalCache == null) {
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.FileCouldNotBeLoadedFreshInit, nameof(GlobalCache)));
|
||||
|
||||
GlobalCache = new GlobalCache();
|
||||
} else {
|
||||
GlobalCache = globalCache;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEnabled && (config == null)) {
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledInConfig, nameof(SteamTokenDumperPlugin)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
config ??= new SteamTokenDumperConfig();
|
||||
|
||||
if (isEnabled) {
|
||||
|
@ -109,8 +128,6 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
|
||||
if (!config.Enabled) {
|
||||
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginDisabledInConfig, nameof(SteamTokenDumperPlugin)));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!config.SecretAppIDs.IsEmpty) {
|
||||
|
@ -125,20 +142,12 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginSecretListInitialized, nameof(config.SecretDepotIDs), string.Join(", ", config.SecretDepotIDs)));
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
GlobalCache? globalCache = await GlobalCache.Load().ConfigureAwait(false);
|
||||
|
||||
if (globalCache == null) {
|
||||
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.FileCouldNotBeLoadedFreshInit, nameof(GlobalCache)));
|
||||
|
||||
GlobalCache = new GlobalCache();
|
||||
} else {
|
||||
GlobalCache = globalCache;
|
||||
}
|
||||
}
|
||||
|
||||
Config = config;
|
||||
|
||||
if (!config.Enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
#pragma warning disable CA5394 // This call isn't used in a security-sensitive manner
|
||||
TimeSpan startIn = TimeSpan.FromMinutes(Random.Shared.Next(SharedInfo.MinimumMinutesBeforeFirstUpload, SharedInfo.MaximumMinutesBeforeFirstUpload));
|
||||
#pragma warning restore CA5394 // This call isn't used in a security-sensitive manner
|
||||
|
@ -151,7 +160,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
ASF.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.PluginInitializedAndEnabled, nameof(SteamTokenDumperPlugin), startIn.ToHumanReadable()));
|
||||
}
|
||||
|
||||
public Task<string?> OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
|
||||
public async Task<string?> OnBotCommand(Bot bot, EAccess access, string message, string[] args, ulong steamID = 0) {
|
||||
ArgumentNullException.ThrowIfNull(bot);
|
||||
|
||||
if (!Enum.IsDefined(access)) {
|
||||
|
@ -162,29 +171,24 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
throw new ArgumentNullException(nameof(args));
|
||||
}
|
||||
|
||||
switch (args[0].ToUpperInvariant()) {
|
||||
case "STD" when access >= EAccess.Owner:
|
||||
if (Config is not { Enabled: true }) {
|
||||
return Task.FromResult((string?) string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, nameof(Config)));
|
||||
switch (args.Length) {
|
||||
case 1:
|
||||
switch (args[0].ToUpperInvariant()) {
|
||||
case "STD":
|
||||
return await ResponseRefreshManually(access, bot).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
TimeSpan minimumTimeBetweenUpload = TimeSpan.FromMinutes(SharedInfo.MinimumMinutesBetweenUploads);
|
||||
|
||||
if (LastUploadAt + minimumTimeBetweenUpload > DateTimeOffset.UtcNow) {
|
||||
return Task.FromResult((string?) string.Format(CultureInfo.CurrentCulture, Strings.SubmissionFailedTooManyRequests, minimumTimeBetweenUpload.ToHumanReadable()));
|
||||
}
|
||||
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (SubmissionSemaphore) {
|
||||
SubmissionTimer.Change(TimeSpan.Zero, TimeSpan.FromHours(SharedInfo.HoursBetweenUploads));
|
||||
}
|
||||
|
||||
return Task.FromResult((string?) ArchiSteamFarm.Localization.Strings.Done);
|
||||
case "STD" when access > EAccess.None:
|
||||
return Task.FromResult((string?) ArchiSteamFarm.Localization.Strings.ErrorAccessDenied);
|
||||
break;
|
||||
default:
|
||||
return Task.FromResult((string?) null);
|
||||
switch (args[0].ToUpperInvariant()) {
|
||||
case "STD":
|
||||
return await ResponseRefreshManually(access, Utilities.GetArgsAsText(args, 1, ","), steamID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task OnBotDestroy(Bot bot) {
|
||||
|
@ -207,10 +211,6 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
public async Task OnBotInit(Bot bot) {
|
||||
ArgumentNullException.ThrowIfNull(bot);
|
||||
|
||||
if (Config is not { Enabled: true }) {
|
||||
return;
|
||||
}
|
||||
|
||||
SemaphoreSlim refreshSemaphore = new(1, 1);
|
||||
Timer refreshTimer = new(OnBotRefreshTimer, bot, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
|
||||
|
||||
|
@ -255,15 +255,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
ArgumentNullException.ThrowIfNull(appChanges);
|
||||
ArgumentNullException.ThrowIfNull(packageChanges);
|
||||
|
||||
if (Config is not { Enabled: true }) {
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
throw new InvalidOperationException(nameof(GlobalCache));
|
||||
}
|
||||
|
||||
GlobalCache.OnPICSChanges(currentChangeNumber, appChanges);
|
||||
GlobalCache?.OnPICSChanges(currentChangeNumber, appChanges);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
@ -271,15 +263,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
public Task OnPICSChangesRestart(uint currentChangeNumber) {
|
||||
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
|
||||
|
||||
if (Config is not { Enabled: true }) {
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
throw new InvalidOperationException(nameof(GlobalCache));
|
||||
}
|
||||
|
||||
GlobalCache.OnPICSChangesRestart(currentChangeNumber);
|
||||
GlobalCache?.OnPICSChangesRestart(currentChangeNumber);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
@ -300,10 +284,6 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
return;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
throw new InvalidOperationException(nameof(GlobalCache));
|
||||
}
|
||||
|
||||
HashSet<uint> packageIDs = callback.LicenseList.Where(static license => !Config.SecretPackageIDs.Contains(license.PackageID) && ((license.PaymentMethod != EPaymentMethod.AutoGrant) || !Config.SkipAutoGrantPackages)).Select(static license => license.PackageID).ToHashSet();
|
||||
|
||||
await Refresh(bot, packageIDs).ConfigureAwait(false);
|
||||
|
@ -314,16 +294,12 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
private static async Task Refresh(Bot bot, IReadOnlyCollection<uint>? packageIDs = null) {
|
||||
ArgumentNullException.ThrowIfNull(bot);
|
||||
|
||||
if (Config is not { Enabled: true }) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
throw new InvalidOperationException(nameof(GlobalCache));
|
||||
}
|
||||
|
||||
if (ASF.GlobalDatabase == null) {
|
||||
throw new InvalidOperationException(nameof(GlobalCache));
|
||||
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
|
||||
}
|
||||
|
||||
if (!BotSynchronizations.TryGetValue(bot, out (SemaphoreSlim RefreshSemaphore, Timer RefreshTimer) synchronization)) {
|
||||
|
@ -341,17 +317,17 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
return;
|
||||
}
|
||||
|
||||
packageIDs ??= bot.OwnedPackageIDs.Where(static package => !Config.SecretPackageIDs.Contains(package.Key) && ((package.Value.PaymentMethod != EPaymentMethod.AutoGrant) || !Config.SkipAutoGrantPackages)).Select(static package => package.Key).ToHashSet();
|
||||
packageIDs ??= bot.OwnedPackageIDs.Where(static package => (Config?.SecretPackageIDs.Contains(package.Key) != true) && ((package.Value.PaymentMethod != EPaymentMethod.AutoGrant) || (Config?.SkipAutoGrantPackages == false))).Select(static package => package.Key).ToHashSet();
|
||||
|
||||
HashSet<uint> appIDsToRefresh = new();
|
||||
|
||||
foreach (uint packageID in packageIDs.Where(static packageID => !Config.SecretPackageIDs.Contains(packageID))) {
|
||||
foreach (uint packageID in packageIDs.Where(static packageID => Config?.SecretPackageIDs.Contains(packageID) != true)) {
|
||||
if (!ASF.GlobalDatabase.PackagesDataReadOnly.TryGetValue(packageID, out PackageData? packageData) || (packageData.AppIDs == null)) {
|
||||
// ASF might not have the package info for us at the moment, we'll retry later
|
||||
continue;
|
||||
}
|
||||
|
||||
appIDsToRefresh.UnionWith(packageData.AppIDs.Where(static appID => !Config.SecretAppIDs.Contains(appID) && GlobalCache.ShouldRefreshAppInfo(appID)));
|
||||
appIDsToRefresh.UnionWith(packageData.AppIDs.Where(static appID => (Config?.SecretAppIDs.Contains(appID) != true) && GlobalCache.ShouldRefreshAppInfo(appID)));
|
||||
}
|
||||
|
||||
if (appIDsToRefresh.Count == 0) {
|
||||
|
@ -456,7 +432,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
bool shouldFetchMainKey = false;
|
||||
|
||||
foreach (KeyValue depot in app.KeyValues["depots"].Children) {
|
||||
if (!uint.TryParse(depot.Name, out uint depotID) || (knownDepotIDs?.Contains(depotID) == true) || Config.SecretDepotIDs.Contains(depotID) || !GlobalCache.ShouldRefreshDepotKey(depotID)) {
|
||||
if (!uint.TryParse(depot.Name, out uint depotID) || (knownDepotIDs?.Contains(depotID) == true) || (Config?.SecretDepotIDs.Contains(depotID) == true) || !GlobalCache.ShouldRefreshDepotKey(depotID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -532,9 +508,11 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
|
||||
bot.ArchiLogger.LogGenericInfo(string.Format(CultureInfo.CurrentCulture, Strings.BotFinishedRetrievingTotalDepots, appIDsToRefresh.Count));
|
||||
} finally {
|
||||
TimeSpan timeSpan = TimeSpan.FromHours(SharedInfo.MaximumHoursBetweenRefresh);
|
||||
if (Config?.Enabled == true) {
|
||||
TimeSpan timeSpan = TimeSpan.FromHours(SharedInfo.MaximumHoursBetweenRefresh);
|
||||
|
||||
synchronization.RefreshTimer.Change(timeSpan, timeSpan);
|
||||
synchronization.RefreshTimer.Change(timeSpan, timeSpan);
|
||||
}
|
||||
|
||||
await depotsRateLimitingSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
|
@ -544,15 +522,65 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
}
|
||||
}
|
||||
|
||||
private static async Task<string?> ResponseRefreshManually(EAccess access, Bot bot, bool submit = true) {
|
||||
if (!Enum.IsDefined(access)) {
|
||||
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
|
||||
}
|
||||
|
||||
ArgumentNullException.ThrowIfNull(bot);
|
||||
|
||||
if (access < EAccess.Master) {
|
||||
return access > EAccess.None ? bot.Commands.FormatBotResponse(ArchiSteamFarm.Localization.Strings.ErrorAccessDenied) : null;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
return bot.Commands.FormatBotResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, nameof(GlobalCache)));
|
||||
}
|
||||
|
||||
await Refresh(bot).ConfigureAwait(false);
|
||||
|
||||
if (submit) {
|
||||
Utilities.InBackground(static () => SubmitData());
|
||||
}
|
||||
|
||||
return bot.Commands.FormatBotResponse(ArchiSteamFarm.Localization.Strings.Done);
|
||||
}
|
||||
|
||||
private static async Task<string?> ResponseRefreshManually(EAccess access, string botNames, ulong steamID = 0) {
|
||||
if (!Enum.IsDefined(access)) {
|
||||
throw new InvalidEnumArgumentException(nameof(access), (int) access, typeof(EAccess));
|
||||
}
|
||||
|
||||
ArgumentException.ThrowIfNullOrEmpty(botNames);
|
||||
|
||||
if ((steamID != 0) && !new SteamID(steamID).IsIndividualAccount) {
|
||||
throw new ArgumentOutOfRangeException(nameof(steamID));
|
||||
}
|
||||
|
||||
HashSet<Bot>? bots = Bot.GetBots(botNames);
|
||||
|
||||
if ((bots == null) || (bots.Count == 0)) {
|
||||
return access >= EAccess.Owner ? Commands.FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.BotNotFound, botNames)) : null;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
return Commands.FormatStaticResponse(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, nameof(GlobalCache)));
|
||||
}
|
||||
|
||||
IList<string?> results = await Utilities.InParallel(bots.Select(bot => ResponseRefreshManually(Commands.GetProxyAccess(bot, access, steamID), bot, false))).ConfigureAwait(false);
|
||||
|
||||
Utilities.InBackground(static () => SubmitData());
|
||||
|
||||
List<string> responses = new(results.Where(static result => !string.IsNullOrEmpty(result))!);
|
||||
|
||||
return responses.Count > 0 ? string.Join(Environment.NewLine, responses) : null;
|
||||
}
|
||||
|
||||
private static async Task SubmitData(CancellationToken cancellationToken = default) {
|
||||
if (Bot.Bots == null) {
|
||||
throw new InvalidOperationException(nameof(Bot.Bots));
|
||||
}
|
||||
|
||||
if (Config is not { Enabled: true }) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (GlobalCache == null) {
|
||||
throw new InvalidOperationException(nameof(GlobalCache));
|
||||
}
|
||||
|
@ -608,7 +636,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.WarningFailedWithError, response.StatusCode));
|
||||
|
||||
switch (response.StatusCode) {
|
||||
case HttpStatusCode.Forbidden:
|
||||
case HttpStatusCode.Forbidden when Config?.Enabled == true:
|
||||
// SteamDB told us to stop submitting data for now
|
||||
// ReSharper disable once SuspiciousLockOverSynchronizationPrimitive - this is not a mistake, we need extra synchronization, and we can re-use the semaphore object for that
|
||||
lock (SubmissionSemaphore) {
|
||||
|
@ -621,7 +649,7 @@ internal sealed class SteamTokenDumperPlugin : OfficialPlugin, IASF, IBot, IBotC
|
|||
GlobalCache.Reset(true);
|
||||
|
||||
break;
|
||||
case HttpStatusCode.TooManyRequests:
|
||||
case HttpStatusCode.TooManyRequests when Config?.Enabled == true:
|
||||
// SteamDB told us to try again later
|
||||
#pragma warning disable CA5394 // This call isn't used in a security-sensitive manner
|
||||
TimeSpan startIn = TimeSpan.FromMinutes(Random.Shared.Next(SharedInfo.MinimumMinutesBeforeFirstUpload, SharedInfo.MaximumMinutesBeforeFirstUpload));
|
||||
|
|
Loading…
Reference in a new issue