2020-06-13 12:08:21 +02:00
|
|
|
// _ _ _ ____ _ _____
|
|
|
|
|
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
|
|
|
|
|
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
|
|
|
|
|
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
|
|
|
|
|
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
|
|
|
|
|
// |
|
2024-01-08 11:33:28 +01:00
|
|
|
// Copyright 2015-2024 Łukasz "JustArchi" Domeradzki
|
2020-06-13 12:08:21 +02:00
|
|
|
// 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.Collections.Concurrent;
|
2024-01-03 13:46:54 +01:00
|
|
|
using System.Collections.Frozen;
|
2020-06-13 12:08:21 +02:00
|
|
|
using System.Collections.Generic;
|
2021-04-25 23:44:47 +02:00
|
|
|
using System.Globalization;
|
2021-08-07 14:03:46 +02:00
|
|
|
using System.IO;
|
2020-06-13 12:08:21 +02:00
|
|
|
using System.Linq;
|
2023-11-14 21:10:35 +01:00
|
|
|
using System.Threading;
|
2020-06-13 12:08:21 +02:00
|
|
|
using System.Threading.Tasks;
|
2021-05-08 01:37:22 +02:00
|
|
|
using ArchiSteamFarm.Core;
|
2020-06-13 12:08:21 +02:00
|
|
|
using ArchiSteamFarm.Helpers;
|
2022-01-06 20:22:38 +01:00
|
|
|
using ArchiSteamFarm.OfficialPlugins.SteamTokenDumper.Localization;
|
2023-01-23 12:08:30 +01:00
|
|
|
using ArchiSteamFarm.Web.Responses;
|
2021-09-15 12:43:25 +02:00
|
|
|
using JetBrains.Annotations;
|
2020-06-13 12:08:21 +02:00
|
|
|
using Newtonsoft.Json;
|
|
|
|
|
using SteamKit2;
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper;
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal sealed class GlobalCache : SerializableFile {
|
2024-01-03 13:46:54 +01:00
|
|
|
internal static readonly ArchiCacheable<FrozenSet<uint>> KnownDepotIDs = new(ResolveKnownDepotIDs, TimeSpan.FromDays(7));
|
2023-01-23 12:08:30 +01:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
private static string SharedFilePath => Path.Combine(ArchiSteamFarm.SharedInfo.ConfigDirectory, $"{nameof(SteamTokenDumper)}.cache");
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
private readonly ConcurrentDictionary<uint, uint> AppChangeNumbers = new();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
private readonly ConcurrentDictionary<uint, ulong> AppTokens = new();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
private readonly ConcurrentDictionary<uint, string> DepotKeys = new();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
private readonly ConcurrentDictionary<uint, ulong> SubmittedApps = new();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
private readonly ConcurrentDictionary<uint, string> SubmittedDepots = new();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
private readonly ConcurrentDictionary<uint, ulong> SubmittedPackages = new();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[JsonProperty(Required = Required.DisallowNull)]
|
|
|
|
|
internal uint LastChangeNumber { get; private set; }
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal GlobalCache() => FilePath = SharedFilePath;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeAppChangeNumbers() => !AppChangeNumbers.IsEmpty;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeAppTokens() => !AppTokens.IsEmpty;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeDepotKeys() => !DepotKeys.IsEmpty;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeLastChangeNumber() => LastChangeNumber > 0;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeSubmittedApps() => !SubmittedApps.IsEmpty;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeSubmittedDepots() => !SubmittedDepots.IsEmpty;
|
2021-09-15 12:43:25 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
[UsedImplicitly]
|
|
|
|
|
public bool ShouldSerializeSubmittedPackages() => !SubmittedPackages.IsEmpty;
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal ulong GetAppToken(uint appID) => AppTokens[appID];
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2023-12-05 01:43:55 +01:00
|
|
|
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);
|
2023-12-05 00:05:16 +01:00
|
|
|
|
|
|
|
|
internal Dictionary<uint, ulong> GetPackageTokensForSubmission() {
|
|
|
|
|
if (ASF.GlobalDatabase == null) {
|
|
|
|
|
throw new InvalidOperationException(nameof(ASF.GlobalDatabase));
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-05 01:43:55 +01:00
|
|
|
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);
|
2023-12-05 00:05:16 +01:00
|
|
|
}
|
2021-04-25 23:44:47 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal static async Task<GlobalCache?> Load() {
|
|
|
|
|
if (!File.Exists(SharedFilePath)) {
|
2021-12-11 15:17:28 +01:00
|
|
|
return new GlobalCache();
|
2021-11-10 21:23:24 +01:00
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2022-01-06 20:22:38 +01:00
|
|
|
ASF.ArchiLogger.LogGenericInfo(Strings.LoadingGlobalCache);
|
2021-12-11 15:11:35 +01:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
GlobalCache? globalCache;
|
2021-04-25 23:44:47 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
try {
|
|
|
|
|
string json = await File.ReadAllTextAsync(SharedFilePath).ConfigureAwait(false);
|
2021-04-25 23:44:47 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
if (string.IsNullOrEmpty(json)) {
|
2022-01-06 20:22:38 +01:00
|
|
|
ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.ErrorIsEmpty, nameof(json)));
|
2021-04-25 23:44:47 +02:00
|
|
|
|
|
|
|
|
return null;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
globalCache = JsonConvert.DeserializeObject<GlobalCache>(json);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
ASF.ArchiLogger.LogGenericException(e);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
return null;
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
if (globalCache == null) {
|
2022-04-13 23:16:36 +02:00
|
|
|
ASF.ArchiLogger.LogNullError(globalCache);
|
2021-11-10 21:23:24 +01:00
|
|
|
|
|
|
|
|
return null;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-06 20:22:38 +01:00
|
|
|
ASF.ArchiLogger.LogGenericInfo(Strings.ValidatingGlobalCacheIntegrity);
|
2021-12-11 15:11:35 +01:00
|
|
|
|
|
|
|
|
if (globalCache.DepotKeys.Values.Any(static depotKey => !IsValidDepotKey(depotKey))) {
|
2022-01-06 20:22:38 +01:00
|
|
|
ASF.ArchiLogger.LogGenericError(Strings.GlobalCacheIntegrityValidationFailed);
|
2021-12-11 15:11:35 +01:00
|
|
|
|
2021-12-11 15:17:28 +01:00
|
|
|
return null;
|
2021-12-11 15:11:35 +01:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
return globalCache;
|
|
|
|
|
}
|
2020-11-14 22:37:00 +01:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal void OnPICSChanges(uint currentChangeNumber, IReadOnlyCollection<KeyValuePair<uint, SteamApps.PICSChangesCallback.PICSChangeData>> appChanges) {
|
2023-11-14 19:12:33 +01:00
|
|
|
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
|
2021-12-12 01:12:54 +01:00
|
|
|
ArgumentNullException.ThrowIfNull(appChanges);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
if (currentChangeNumber <= LastChangeNumber) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
LastChangeNumber = currentChangeNumber;
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach ((uint appID, SteamApps.PICSChangesCallback.PICSChangeData appData) in appChanges) {
|
2022-02-26 01:21:37 +01:00
|
|
|
if (!AppChangeNumbers.TryGetValue(appID, out uint previousChangeNumber) || (previousChangeNumber >= appData.ChangeNumber)) {
|
2021-11-10 21:23:24 +01:00
|
|
|
continue;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
AppChangeNumbers.TryRemove(appID, out _);
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
Utilities.InBackground(Save);
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal void OnPICSChangesRestart(uint currentChangeNumber) {
|
2023-11-14 19:12:33 +01:00
|
|
|
ArgumentOutOfRangeException.ThrowIfZero(currentChangeNumber);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
if (currentChangeNumber <= LastChangeNumber) {
|
|
|
|
|
return;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
LastChangeNumber = currentChangeNumber;
|
2022-10-10 22:38:35 +02:00
|
|
|
|
|
|
|
|
Reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void Reset(bool clear = false) {
|
2021-11-10 21:23:24 +01:00
|
|
|
AppChangeNumbers.Clear();
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2022-10-10 22:38:35 +02:00
|
|
|
if (clear) {
|
|
|
|
|
AppTokens.Clear();
|
|
|
|
|
DepotKeys.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
Utilities.InBackground(Save);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal bool ShouldRefreshAppInfo(uint appID) => !AppChangeNumbers.ContainsKey(appID);
|
|
|
|
|
internal bool ShouldRefreshDepotKey(uint depotID) => !DepotKeys.ContainsKey(depotID);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal void UpdateAppChangeNumbers(IReadOnlyCollection<KeyValuePair<uint, uint>> appChangeNumbers) {
|
2021-12-12 01:12:54 +01:00
|
|
|
ArgumentNullException.ThrowIfNull(appChangeNumbers);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
bool save = false;
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach ((uint appID, uint changeNumber) in appChangeNumbers) {
|
2022-02-26 01:21:37 +01:00
|
|
|
if (AppChangeNumbers.TryGetValue(appID, out uint previousChangeNumber) && (previousChangeNumber >= changeNumber)) {
|
2021-11-10 21:23:24 +01:00
|
|
|
continue;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
AppChangeNumbers[appID] = changeNumber;
|
|
|
|
|
save = true;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
if (save) {
|
|
|
|
|
Utilities.InBackground(Save);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-11-14 22:37:00 +01:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal void UpdateAppTokens(IReadOnlyCollection<KeyValuePair<uint, ulong>> appTokens, IReadOnlyCollection<uint> publicAppIDs) {
|
2021-12-12 01:12:54 +01:00
|
|
|
ArgumentNullException.ThrowIfNull(appTokens);
|
|
|
|
|
ArgumentNullException.ThrowIfNull(publicAppIDs);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
bool save = false;
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach ((uint appID, ulong appToken) in appTokens) {
|
|
|
|
|
if (AppTokens.TryGetValue(appID, out ulong previousAppToken) && (previousAppToken == appToken)) {
|
|
|
|
|
continue;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
AppTokens[appID] = appToken;
|
|
|
|
|
save = true;
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach (uint appID in publicAppIDs) {
|
|
|
|
|
if (AppTokens.TryGetValue(appID, out ulong previousAppToken) && (previousAppToken == 0)) {
|
|
|
|
|
continue;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
AppTokens[appID] = 0;
|
|
|
|
|
save = true;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
if (save) {
|
|
|
|
|
Utilities.InBackground(Save);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2023-01-21 21:16:01 +01:00
|
|
|
internal void UpdateDepotKey(SteamApps.DepotKeyCallback depotKeyResult) {
|
|
|
|
|
ArgumentNullException.ThrowIfNull(depotKeyResult);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2023-01-21 21:16:01 +01:00
|
|
|
if (depotKeyResult.Result != EResult.OK) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-12-11 15:11:35 +01:00
|
|
|
|
2023-01-21 21:16:01 +01:00
|
|
|
string depotKey = Convert.ToHexString(depotKeyResult.DepotKey);
|
2021-12-11 15:11:35 +01:00
|
|
|
|
2023-01-21 21:16:01 +01:00
|
|
|
if (!IsValidDepotKey(depotKey)) {
|
|
|
|
|
ASF.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, ArchiSteamFarm.Localization.Strings.ErrorIsInvalid, nameof(depotKey)));
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2023-01-21 21:16:01 +01:00
|
|
|
return;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
|
|
|
|
|
2023-01-21 21:16:01 +01:00
|
|
|
if (DepotKeys.TryGetValue(depotKeyResult.DepotID, out string? previousDepotKey) && (previousDepotKey == depotKey)) {
|
|
|
|
|
return;
|
2021-11-10 21:23:24 +01:00
|
|
|
}
|
2023-01-21 21:16:01 +01:00
|
|
|
|
|
|
|
|
DepotKeys[depotKeyResult.DepotID] = depotKey;
|
|
|
|
|
|
|
|
|
|
Utilities.InBackground(Save);
|
2021-11-10 21:23:24 +01:00
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
internal void UpdateSubmittedData(IReadOnlyDictionary<uint, ulong> apps, IReadOnlyDictionary<uint, ulong> packages, IReadOnlyDictionary<uint, string> depots) {
|
2021-12-12 01:12:54 +01:00
|
|
|
ArgumentNullException.ThrowIfNull(apps);
|
|
|
|
|
ArgumentNullException.ThrowIfNull(packages);
|
|
|
|
|
ArgumentNullException.ThrowIfNull(depots);
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2022-02-26 01:26:13 +01:00
|
|
|
bool save = false;
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach ((uint appID, ulong token) in apps) {
|
2022-02-26 01:26:13 +01:00
|
|
|
if (SubmittedApps.TryGetValue(appID, out ulong previousToken) && (previousToken == token)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
SubmittedApps[appID] = token;
|
2022-02-26 01:26:13 +01:00
|
|
|
save = true;
|
2021-11-10 21:23:24 +01:00
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach ((uint packageID, ulong token) in packages) {
|
2022-02-26 01:26:13 +01:00
|
|
|
if (SubmittedPackages.TryGetValue(packageID, out ulong previousToken) && (previousToken == token)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
SubmittedPackages[packageID] = token;
|
2022-02-26 01:26:13 +01:00
|
|
|
save = true;
|
2021-11-10 21:23:24 +01:00
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
foreach ((uint depotID, string key) in depots) {
|
2022-02-26 01:26:13 +01:00
|
|
|
if (SubmittedDepots.TryGetValue(depotID, out string? previousKey) && (previousKey == key)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 21:23:24 +01:00
|
|
|
SubmittedDepots[depotID] = key;
|
2022-02-26 01:26:13 +01:00
|
|
|
save = true;
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
2021-11-10 21:23:24 +01:00
|
|
|
|
2022-02-26 01:26:13 +01:00
|
|
|
if (save) {
|
|
|
|
|
Utilities.InBackground(Save);
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|
2021-12-11 15:11:35 +01:00
|
|
|
|
|
|
|
|
private static bool IsValidDepotKey(string depotKey) {
|
2023-11-14 19:12:33 +01:00
|
|
|
ArgumentException.ThrowIfNullOrEmpty(depotKey);
|
2021-12-11 15:11:35 +01:00
|
|
|
|
|
|
|
|
return (depotKey.Length == 64) && Utilities.IsValidHexadecimalText(depotKey);
|
|
|
|
|
}
|
2023-01-23 12:08:30 +01:00
|
|
|
|
Closes #3133
After investigation, it turns out that the token actually has correct scope (THANK GOD), it's the fact that Valve started issuing those on much shorter notice than our cache.
Up until now, we played it smartly by assuming cached access token should be valid for at least 6 hours, since every time we visited the page, we got a new token that was valid for 24h since issuing. This however is no longer the case and Valve seems to recycle the same token for every request now, probably until we get close to its expiration. This also means that with unlucky timing, we might be trying to use access token that has expired already even for up to 6 more hours, which is unwanted and causes all kind of issues, with 403 in trade offers being one of them.
I could make stupid solution and cache token for shorter, e.g. 1 minute, but instead I did 200 IQ move and rewrote the functionality in a way to actually parse that token, its validity, and set the cache to be valid for a brief moment before the token actually expires. This way, we're not only more efficient (we can cache the token even for 24h if needed), but we're also invalidating it as soon as it goes out of the scope.
2024-01-29 17:53:46 +01:00
|
|
|
private static async Task<(bool Success, FrozenSet<uint>? Result, DateTime? ValidUntil)> ResolveKnownDepotIDs(CancellationToken cancellationToken = default) {
|
2023-01-23 12:08:30 +01:00
|
|
|
if (ASF.WebBrowser == null) {
|
|
|
|
|
throw new InvalidOperationException(nameof(ASF.WebBrowser));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uri request = new($"{SharedInfo.ServerURL}/knowndepots.csv");
|
|
|
|
|
|
2023-11-14 21:10:35 +01:00
|
|
|
StreamResponse? response = await ASF.WebBrowser.UrlGetToStream(request, cancellationToken: cancellationToken).ConfigureAwait(false);
|
2023-01-23 12:08:30 +01:00
|
|
|
|
|
|
|
|
if (response?.Content == null) {
|
Closes #3133
After investigation, it turns out that the token actually has correct scope (THANK GOD), it's the fact that Valve started issuing those on much shorter notice than our cache.
Up until now, we played it smartly by assuming cached access token should be valid for at least 6 hours, since every time we visited the page, we got a new token that was valid for 24h since issuing. This however is no longer the case and Valve seems to recycle the same token for every request now, probably until we get close to its expiration. This also means that with unlucky timing, we might be trying to use access token that has expired already even for up to 6 more hours, which is unwanted and causes all kind of issues, with 403 in trade offers being one of them.
I could make stupid solution and cache token for shorter, e.g. 1 minute, but instead I did 200 IQ move and rewrote the functionality in a way to actually parse that token, its validity, and set the cache to be valid for a brief moment before the token actually expires. This way, we're not only more efficient (we can cache the token even for 24h if needed), but we're also invalidating it as soon as it goes out of the scope.
2024-01-29 17:53:46 +01:00
|
|
|
return (false, null, null);
|
2023-01-23 12:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
await using (response.ConfigureAwait(false)) {
|
|
|
|
|
try {
|
|
|
|
|
using StreamReader reader = new(response.Content);
|
|
|
|
|
|
2023-11-14 21:10:35 +01:00
|
|
|
string? countText = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false);
|
2023-01-23 12:08:30 +01:00
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(countText) || !int.TryParse(countText, out int count) || (count <= 0)) {
|
2023-10-30 22:44:07 +01:00
|
|
|
ASF.ArchiLogger.LogNullError(countText);
|
2023-01-23 12:08:30 +01:00
|
|
|
|
Closes #3133
After investigation, it turns out that the token actually has correct scope (THANK GOD), it's the fact that Valve started issuing those on much shorter notice than our cache.
Up until now, we played it smartly by assuming cached access token should be valid for at least 6 hours, since every time we visited the page, we got a new token that was valid for 24h since issuing. This however is no longer the case and Valve seems to recycle the same token for every request now, probably until we get close to its expiration. This also means that with unlucky timing, we might be trying to use access token that has expired already even for up to 6 more hours, which is unwanted and causes all kind of issues, with 403 in trade offers being one of them.
I could make stupid solution and cache token for shorter, e.g. 1 minute, but instead I did 200 IQ move and rewrote the functionality in a way to actually parse that token, its validity, and set the cache to be valid for a brief moment before the token actually expires. This way, we're not only more efficient (we can cache the token even for 24h if needed), but we're also invalidating it as soon as it goes out of the scope.
2024-01-29 17:53:46 +01:00
|
|
|
return (false, null, null);
|
2023-01-23 12:08:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HashSet<uint> result = new(count);
|
|
|
|
|
|
2023-11-14 21:10:35 +01:00
|
|
|
while (await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false) is { Length: > 0 } line) {
|
2023-01-23 12:08:30 +01:00
|
|
|
if (!uint.TryParse(line, out uint depotID) || (depotID == 0)) {
|
2023-10-30 22:44:07 +01:00
|
|
|
ASF.ArchiLogger.LogNullError(depotID);
|
2023-01-23 12:08:30 +01:00
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.Add(depotID);
|
|
|
|
|
}
|
|
|
|
|
|
Closes #3133
After investigation, it turns out that the token actually has correct scope (THANK GOD), it's the fact that Valve started issuing those on much shorter notice than our cache.
Up until now, we played it smartly by assuming cached access token should be valid for at least 6 hours, since every time we visited the page, we got a new token that was valid for 24h since issuing. This however is no longer the case and Valve seems to recycle the same token for every request now, probably until we get close to its expiration. This also means that with unlucky timing, we might be trying to use access token that has expired already even for up to 6 more hours, which is unwanted and causes all kind of issues, with 403 in trade offers being one of them.
I could make stupid solution and cache token for shorter, e.g. 1 minute, but instead I did 200 IQ move and rewrote the functionality in a way to actually parse that token, its validity, and set the cache to be valid for a brief moment before the token actually expires. This way, we're not only more efficient (we can cache the token even for 24h if needed), but we're also invalidating it as soon as it goes out of the scope.
2024-01-29 17:53:46 +01:00
|
|
|
return (result.Count > 0, result.ToFrozenSet(), null);
|
2023-01-23 12:08:30 +01:00
|
|
|
} catch (Exception e) {
|
|
|
|
|
ASF.ArchiLogger.LogGenericWarningException(e);
|
|
|
|
|
|
Closes #3133
After investigation, it turns out that the token actually has correct scope (THANK GOD), it's the fact that Valve started issuing those on much shorter notice than our cache.
Up until now, we played it smartly by assuming cached access token should be valid for at least 6 hours, since every time we visited the page, we got a new token that was valid for 24h since issuing. This however is no longer the case and Valve seems to recycle the same token for every request now, probably until we get close to its expiration. This also means that with unlucky timing, we might be trying to use access token that has expired already even for up to 6 more hours, which is unwanted and causes all kind of issues, with 403 in trade offers being one of them.
I could make stupid solution and cache token for shorter, e.g. 1 minute, but instead I did 200 IQ move and rewrote the functionality in a way to actually parse that token, its validity, and set the cache to be valid for a brief moment before the token actually expires. This way, we're not only more efficient (we can cache the token even for 24h if needed), but we're also invalidating it as soon as it goes out of the scope.
2024-01-29 17:53:46 +01:00
|
|
|
return (false, null, null);
|
2023-01-23 12:08:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-06-13 12:08:21 +02:00
|
|
|
}
|