2020-06-13 10:08:21 +00:00
// _ _ _ ____ _ _____
// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___
// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \
// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | |
// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_|
// |
2021-01-03 21:24:22 +00:00
// Copyright 2015-2021 Łukasz "JustArchi" Domeradzki
2020-06-13 10:08:21 +00: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 ;
using System.Collections.Generic ;
2021-01-03 20:37:16 +00:00
using System.Collections.Immutable ;
2020-06-13 10:08:21 +00:00
using System.Composition ;
2020-11-15 23:44:37 +00:00
using System.Globalization ;
2020-06-13 10:08:21 +00:00
using System.Linq ;
using System.Net ;
using System.Threading ;
using System.Threading.Tasks ;
using ArchiSteamFarm.Localization ;
using ArchiSteamFarm.Plugins ;
2020-06-24 18:54:41 +00:00
using Newtonsoft.Json ;
2020-06-13 10:08:21 +00:00
using Newtonsoft.Json.Linq ;
using SteamKit2 ;
namespace ArchiSteamFarm.OfficialPlugins.SteamTokenDumper {
[Export(typeof(IPlugin))]
2020-06-13 13:09:12 +00:00
internal sealed class SteamTokenDumperPlugin : OfficialPlugin , IASF , IBot , IBotSteamClient , ISteamPICSChanges {
2020-11-14 21:37:00 +00:00
private static readonly ConcurrentDictionary < Bot , IDisposable > BotSubscriptions = new ( ) ;
private static readonly ConcurrentDictionary < Bot , ( SemaphoreSlim RefreshSemaphore , Timer RefreshTimer ) > BotSynchronizations = new ( ) ;
private static readonly SemaphoreSlim SubmissionSemaphore = new ( 1 , 1 ) ;
private static readonly Timer SubmissionTimer = new ( SubmitData ) ;
2020-06-13 10:08:21 +00:00
2020-08-22 19:41:01 +00:00
private static GlobalCache ? GlobalCache ;
2020-06-24 18:49:55 +00:00
2020-06-24 18:54:41 +00:00
[JsonProperty]
2020-06-13 10:08:21 +00:00
private static bool IsEnabled ;
2020-06-24 18:54:41 +00:00
[JsonProperty]
2020-06-13 13:09:12 +00:00
public override string Name = > nameof ( SteamTokenDumperPlugin ) ;
2020-06-13 10:08:21 +00:00
2020-06-24 18:54:41 +00:00
[JsonProperty]
2020-11-15 23:44:37 +00:00
public override Version Version = > typeof ( SteamTokenDumperPlugin ) . Assembly . GetName ( ) . Version ? ? throw new InvalidOperationException ( nameof ( Version ) ) ;
2020-06-13 10:08:21 +00:00
public Task < uint > GetPreferredChangeNumberToStartFrom ( ) = > Task . FromResult ( IsEnabled ? GlobalCache ? . LastChangeNumber ? ? 0 : 0 ) ;
2020-12-02 23:10:52 +00:00
public async void OnASFInit ( IReadOnlyDictionary < string , JToken > ? additionalConfigProperties = null ) {
2020-06-13 13:35:56 +00:00
if ( ! SharedInfo . HasValidToken ) {
ASF . ArchiLogger . LogGenericError ( $"{Name} has been disabled due to missing build token." ) ;
return ;
}
2020-06-13 10:08:21 +00:00
bool enabled = false ;
if ( additionalConfigProperties ! = null ) {
foreach ( ( string configProperty , JToken configValue ) in additionalConfigProperties ) {
try {
2020-06-24 18:49:55 +00:00
if ( configProperty = = nameof ( GlobalConfigExtension . SteamTokenDumperPluginEnabled ) ) {
2020-06-13 10:08:21 +00:00
enabled = configValue . Value < bool > ( ) ;
}
} catch ( Exception e ) {
ASF . ArchiLogger . LogGenericException ( e ) ;
break ;
}
}
}
IsEnabled = enabled ;
if ( ! enabled ) {
2020-06-13 16:47:09 +00:00
ASF . ArchiLogger . LogGenericInfo ( $"{Name} is currently disabled. If you'd like to help SteamDB in data submission, check out our wiki for {nameof(SteamTokenDumperPlugin)}." ) ;
2020-06-13 10:08:21 +00:00
return ;
}
2020-12-02 23:10:52 +00:00
GlobalCache ? ? = await GlobalCache . Load ( ) . ConfigureAwait ( false ) ;
2020-06-13 10:08:21 +00:00
TimeSpan startIn = TimeSpan . FromMinutes ( Utilities . RandomNext ( SharedInfo . MinimumMinutesBeforeFirstUpload , SharedInfo . MaximumMinutesBeforeFirstUpload ) ) ;
lock ( SubmissionTimer ) {
SubmissionTimer . Change ( startIn , TimeSpan . FromHours ( SharedInfo . MinimumHoursBetweenUploads ) ) ;
}
ASF . ArchiLogger . LogGenericInfo ( $"{Name} has been initialized successfully, thank you for your help. The first submission will happen in approximately {startIn.ToHumanReadable()} from now." ) ;
}
public async void OnBotDestroy ( Bot bot ) {
if ( bot = = null ) {
throw new ArgumentNullException ( nameof ( bot ) ) ;
}
2020-08-22 19:41:01 +00:00
if ( BotSubscriptions . TryRemove ( bot , out IDisposable ? subscription ) ) {
2020-06-13 10:08:21 +00:00
subscription . Dispose ( ) ;
}
if ( BotSynchronizations . TryRemove ( bot , out ( SemaphoreSlim RefreshSemaphore , Timer RefreshTimer ) synchronization ) ) {
synchronization . RefreshSemaphore . Dispose ( ) ;
await synchronization . RefreshTimer . DisposeAsync ( ) . ConfigureAwait ( false ) ;
}
}
public async void OnBotInit ( Bot bot ) {
if ( bot = = null ) {
throw new ArgumentNullException ( nameof ( bot ) ) ;
}
if ( ! IsEnabled ) {
return ;
}
2020-11-14 21:37:00 +00:00
SemaphoreSlim refreshSemaphore = new ( 1 , 1 ) ;
Timer refreshTimer = new ( async _ = > await Refresh ( bot ) . ConfigureAwait ( false ) ) ;
2020-06-13 10:08:21 +00:00
if ( ! BotSynchronizations . TryAdd ( bot , ( refreshSemaphore , refreshTimer ) ) ) {
refreshSemaphore . Dispose ( ) ;
await refreshTimer . DisposeAsync ( ) . ConfigureAwait ( false ) ;
}
}
public void OnBotSteamCallbacksInit ( Bot bot , CallbackManager callbackManager ) {
2020-11-14 21:37:00 +00:00
if ( bot = = null ) {
throw new ArgumentNullException ( nameof ( bot ) ) ;
}
if ( callbackManager = = null ) {
throw new ArgumentNullException ( nameof ( callbackManager ) ) ;
2020-06-13 10:08:21 +00:00
}
2020-08-22 19:41:01 +00:00
if ( BotSubscriptions . TryRemove ( bot , out IDisposable ? subscription ) ) {
2020-06-13 10:08:21 +00:00
subscription . Dispose ( ) ;
}
if ( ! IsEnabled ) {
return ;
}
subscription = callbackManager . Subscribe < SteamApps . LicenseListCallback > ( callback = > OnLicenseList ( bot , callback ) ) ;
if ( ! BotSubscriptions . TryAdd ( bot , subscription ) ) {
subscription . Dispose ( ) ;
}
}
2020-08-22 19:41:01 +00:00
public IReadOnlyCollection < ClientMsgHandler > ? OnBotSteamHandlersInit ( Bot bot ) = > null ;
2020-06-13 10:08:21 +00:00
2020-06-13 13:09:12 +00:00
public override void OnLoaded ( ) { }
2020-06-13 10:08:21 +00:00
2021-01-03 21:02:24 +00:00
public void OnPICSChanges ( uint currentChangeNumber , IReadOnlyDictionary < uint , SteamApps . PICSChangesCallback . PICSChangeData > appChanges , IReadOnlyDictionary < uint , SteamApps . PICSChangesCallback . PICSChangeData > packageChanges ) {
2020-11-14 21:37:00 +00:00
if ( currentChangeNumber = = 0 ) {
throw new ArgumentOutOfRangeException ( nameof ( currentChangeNumber ) ) ;
}
if ( appChanges = = null ) {
throw new ArgumentNullException ( nameof ( appChanges ) ) ;
}
if ( packageChanges = = null ) {
throw new ArgumentNullException ( nameof ( packageChanges ) ) ;
2020-06-13 10:08:21 +00:00
}
if ( ! IsEnabled ) {
return ;
}
if ( GlobalCache = = null ) {
2020-11-15 23:44:37 +00:00
throw new InvalidOperationException ( nameof ( GlobalCache ) ) ;
2020-06-13 10:08:21 +00:00
}
2021-01-03 21:02:24 +00:00
GlobalCache . OnPICSChanges ( currentChangeNumber , appChanges ) ;
2020-06-13 10:08:21 +00:00
}
2021-01-03 21:02:24 +00:00
public void OnPICSChangesRestart ( uint currentChangeNumber ) {
2020-06-13 10:08:21 +00:00
if ( currentChangeNumber = = 0 ) {
2020-11-14 21:37:00 +00:00
throw new ArgumentOutOfRangeException ( nameof ( currentChangeNumber ) ) ;
2020-06-13 10:08:21 +00:00
}
if ( ! IsEnabled ) {
return ;
}
if ( GlobalCache = = null ) {
2020-11-14 21:37:00 +00:00
throw new InvalidOperationException ( nameof ( GlobalCache ) ) ;
2020-06-13 10:08:21 +00:00
}
2021-01-03 21:02:24 +00:00
GlobalCache . OnPICSChangesRestart ( currentChangeNumber ) ;
2020-06-13 10:08:21 +00:00
}
2020-08-22 19:41:01 +00:00
private static async void OnLicenseList ( Bot bot , SteamApps . LicenseListCallback callback ) {
2020-11-14 21:37:00 +00:00
if ( bot = = null ) {
throw new ArgumentNullException ( nameof ( bot ) ) ;
}
if ( callback = = null ) {
2020-06-13 10:08:21 +00:00
throw new ArgumentNullException ( nameof ( callback ) ) ;
}
if ( ! IsEnabled ) {
return ;
}
if ( GlobalCache = = null ) {
2020-11-14 21:37:00 +00:00
throw new InvalidOperationException ( nameof ( GlobalCache ) ) ;
2020-06-13 10:08:21 +00:00
}
2020-06-13 19:45:37 +00:00
Dictionary < uint , ulong > packageTokens = callback . LicenseList . GroupBy ( license = > license . PackageID ) . ToDictionary ( group = > group . Key , group = > group . OrderByDescending ( license = > license . TimeCreated ) . First ( ) . AccessToken ) ;
2020-06-13 10:08:21 +00:00
2021-01-03 21:02:24 +00:00
GlobalCache . UpdatePackageTokens ( packageTokens ) ;
2020-06-13 10:08:21 +00:00
await Refresh ( bot , packageTokens . Keys ) . ConfigureAwait ( false ) ;
}
2020-08-22 19:41:01 +00:00
private static async Task Refresh ( Bot bot , IReadOnlyCollection < uint > ? packageIDs = null ) {
2020-06-13 10:08:21 +00:00
if ( bot = = null ) {
throw new ArgumentNullException ( nameof ( bot ) ) ;
}
if ( ! IsEnabled ) {
return ;
}
2020-11-14 21:37:00 +00:00
if ( GlobalCache = = null ) {
throw new InvalidOperationException ( nameof ( GlobalCache ) ) ;
}
if ( ASF . GlobalDatabase = = null ) {
throw new InvalidOperationException ( nameof ( GlobalCache ) ) ;
2020-06-13 10:08:21 +00:00
}
if ( ! BotSynchronizations . TryGetValue ( bot , out ( SemaphoreSlim RefreshSemaphore , Timer RefreshTimer ) synchronization ) ) {
2020-11-15 23:44:37 +00:00
throw new InvalidOperationException ( nameof ( synchronization ) ) ;
2020-06-13 10:08:21 +00:00
}
if ( ! await synchronization . RefreshSemaphore . WaitAsync ( 0 ) . ConfigureAwait ( false ) ) {
return ;
}
try {
if ( ! bot . IsConnectedAndLoggedOn ) {
return ;
}
packageIDs ? ? = bot . OwnedPackageIDsReadOnly ;
2020-11-14 21:37:00 +00:00
HashSet < uint > appIDsToRefresh = new ( ) ;
2020-06-13 10:08:21 +00:00
foreach ( uint packageID in packageIDs ) {
2021-01-03 20:37:16 +00:00
if ( ! ASF . GlobalDatabase . PackagesDataReadOnly . TryGetValue ( packageID , out ( uint ChangeNumber , ImmutableHashSet < uint > ? AppIDs ) packageData ) | | ( packageData . AppIDs = = null ) ) {
2020-06-13 10:08:21 +00:00
// ASF might not have the package info for us at the moment, we'll retry later
continue ;
}
appIDsToRefresh . UnionWith ( packageData . AppIDs . Where ( appID = > GlobalCache . ShouldRefreshAppInfo ( appID ) ) ) ;
}
if ( appIDsToRefresh . Count = = 0 ) {
bot . ArchiLogger . LogGenericDebug ( $"There are no apps to refresh for {bot.BotName}." ) ;
return ;
}
bot . ArchiLogger . LogGenericInfo ( $"Retrieving a total of {appIDsToRefresh.Count} app access tokens..." ) ;
2020-11-14 21:37:00 +00:00
HashSet < uint > appIDsThisRound = new ( Math . Min ( appIDsToRefresh . Count , SharedInfo . AppInfosPerSingleRequest ) ) ;
2020-06-13 10:08:21 +00:00
using ( HashSet < uint > . Enumerator enumerator = appIDsToRefresh . GetEnumerator ( ) ) {
while ( true ) {
2020-06-15 14:25:28 +00:00
while ( ( appIDsThisRound . Count < SharedInfo . AppInfosPerSingleRequest ) & & enumerator . MoveNext ( ) ) {
2020-06-13 10:08:21 +00:00
appIDsThisRound . Add ( enumerator . Current ) ;
}
if ( appIDsThisRound . Count = = 0 ) {
break ;
}
2020-06-20 15:12:18 +00:00
if ( ! bot . IsConnectedAndLoggedOn ) {
return ;
}
2020-06-13 10:08:21 +00:00
bot . ArchiLogger . LogGenericInfo ( $"Retrieving {appIDsThisRound.Count} app access tokens..." ) ;
SteamApps . PICSTokensCallback response ;
try {
2020-06-15 14:55:57 +00:00
response = await bot . SteamApps . PICSGetAccessTokens ( appIDsThisRound , Enumerable . Empty < uint > ( ) ) . ToLongRunningTask ( ) . ConfigureAwait ( false ) ;
2020-06-13 10:08:21 +00:00
} catch ( Exception e ) {
bot . ArchiLogger . LogGenericWarningException ( e ) ;
return ;
}
2020-06-20 15:12:18 +00:00
if ( response = = null ) {
2020-11-15 23:44:37 +00:00
bot . ArchiLogger . LogGenericWarning ( string . Format ( CultureInfo . CurrentCulture , Strings . WarningFailedWithError , nameof ( response ) ) ) ;
2020-06-20 15:12:18 +00:00
return ;
}
2020-06-13 10:08:21 +00:00
bot . ArchiLogger . LogGenericInfo ( $"Finished retrieving {appIDsThisRound.Count} app access tokens." ) ;
appIDsThisRound . Clear ( ) ;
2021-01-03 21:02:24 +00:00
GlobalCache . UpdateAppTokens ( response . AppTokens , response . AppTokensDenied ) ;
2020-06-13 10:08:21 +00:00
}
}
bot . ArchiLogger . LogGenericInfo ( $"Finished retrieving a total of {appIDsToRefresh.Count} app access tokens." ) ;
bot . ArchiLogger . LogGenericInfo ( $"Retrieving all depots for a total of {appIDsToRefresh.Count} apps..." ) ;
appIDsThisRound . Clear ( ) ;
using ( HashSet < uint > . Enumerator enumerator = appIDsToRefresh . GetEnumerator ( ) ) {
while ( true ) {
2020-06-15 14:25:28 +00:00
while ( ( appIDsThisRound . Count < SharedInfo . AppInfosPerSingleRequest ) & & enumerator . MoveNext ( ) ) {
2020-06-13 10:08:21 +00:00
appIDsThisRound . Add ( enumerator . Current ) ;
}
if ( appIDsThisRound . Count = = 0 ) {
break ;
}
2020-06-20 15:12:18 +00:00
if ( ! bot . IsConnectedAndLoggedOn ) {
return ;
}
2020-06-13 10:08:21 +00:00
bot . ArchiLogger . LogGenericInfo ( $"Retrieving {appIDsThisRound.Count} app infos..." ) ;
AsyncJobMultiple < SteamApps . PICSProductInfoCallback > . ResultSet response ;
try {
2020-06-15 14:55:57 +00:00
response = await bot . SteamApps . PICSGetProductInfo ( appIDsThisRound . Select ( appID = > new SteamApps . PICSRequest { ID = appID , AccessToken = GlobalCache . GetAppToken ( appID ) , Public = false } ) , Enumerable . Empty < SteamApps . PICSRequest > ( ) ) . ToLongRunningTask ( ) . ConfigureAwait ( false ) ;
2020-06-13 10:08:21 +00:00
} catch ( Exception e ) {
bot . ArchiLogger . LogGenericWarningException ( e ) ;
return ;
}
if ( response . Results = = null ) {
2020-11-15 23:44:37 +00:00
bot . ArchiLogger . LogGenericWarning ( string . Format ( CultureInfo . CurrentCulture , Strings . WarningFailedWithError , nameof ( response . Results ) ) ) ;
2020-06-13 10:08:21 +00:00
return ;
}
bot . ArchiLogger . LogGenericInfo ( $"Finished retrieving {appIDsThisRound.Count} app infos." ) ;
appIDsThisRound . Clear ( ) ;
2020-11-14 21:37:00 +00:00
Dictionary < uint , uint > appChangeNumbers = new ( ) ;
2020-06-15 14:25:28 +00:00
2020-11-14 21:37:00 +00:00
HashSet < Task < SteamApps . DepotKeyCallback > > depotTasks = new ( ) ;
2020-06-13 10:08:21 +00:00
foreach ( SteamApps . PICSProductInfoCallback . PICSProductInfo app in response . Results . SelectMany ( result = > result . Apps . Values ) ) {
appChangeNumbers [ app . ID ] = app . ChangeNumber ;
if ( GlobalCache . ShouldRefreshDepotKey ( app . ID ) ) {
depotTasks . Add ( bot . SteamApps . GetDepotDecryptionKey ( app . ID , app . ID ) . ToLongRunningTask ( ) ) ;
}
foreach ( KeyValue depot in app . KeyValues [ "depots" ] . Children ) {
if ( uint . TryParse ( depot . Name , out uint depotID ) & & GlobalCache . ShouldRefreshDepotKey ( depotID ) ) {
depotTasks . Add ( bot . SteamApps . GetDepotDecryptionKey ( depotID , app . ID ) . ToLongRunningTask ( ) ) ;
}
}
}
2021-01-03 21:02:24 +00:00
GlobalCache . UpdateAppChangeNumbers ( appChangeNumbers ) ;
2020-06-13 10:08:21 +00:00
if ( depotTasks . Count > 0 ) {
bot . ArchiLogger . LogGenericInfo ( $"Retrieving {depotTasks.Count} depot keys..." ) ;
2020-07-15 14:43:59 +00:00
IList < SteamApps . DepotKeyCallback > results ;
2020-06-25 15:53:37 +00:00
try {
2020-07-15 14:43:59 +00:00
results = await Utilities . InParallel ( depotTasks ) . ConfigureAwait ( false ) ;
2020-06-25 15:53:37 +00:00
} catch ( Exception e ) {
bot . ArchiLogger . LogGenericWarningException ( e ) ;
return ;
}
2020-06-13 10:08:21 +00:00
bot . ArchiLogger . LogGenericInfo ( $"Finished retrieving {depotTasks.Count} depot keys." ) ;
2021-01-03 21:02:24 +00:00
GlobalCache . UpdateDepotKeys ( results ) ;
2020-06-13 10:08:21 +00:00
}
}
}
bot . ArchiLogger . LogGenericInfo ( $"Finished retrieving all depot keys for a total of {appIDsToRefresh.Count} apps." ) ;
} finally {
TimeSpan timeSpan = TimeSpan . FromHours ( SharedInfo . MaximumHoursBetweenRefresh ) ;
synchronization . RefreshTimer . Change ( timeSpan , timeSpan ) ;
synchronization . RefreshSemaphore . Release ( ) ;
}
}
2020-11-14 21:37:00 +00:00
private static async void SubmitData ( object? state ) {
2020-08-22 19:41:01 +00:00
if ( Bot . Bots = = null ) {
2020-11-14 21:37:00 +00:00
throw new InvalidOperationException ( nameof ( Bot . Bots ) ) ;
2020-08-22 19:41:01 +00:00
}
2020-06-13 10:08:21 +00:00
const string request = SharedInfo . ServerURL + "/submit" ;
if ( ! IsEnabled ) {
return ;
}
2020-11-14 21:37:00 +00:00
if ( GlobalCache = = null ) {
throw new InvalidOperationException ( nameof ( GlobalCache ) ) ;
}
if ( ASF . GlobalConfig = = null ) {
throw new InvalidOperationException ( nameof ( ASF . GlobalConfig ) ) ;
}
if ( ASF . WebBrowser = = null ) {
throw new InvalidOperationException ( nameof ( ASF . WebBrowser ) ) ;
2020-06-13 10:08:21 +00:00
}
if ( ! await SubmissionSemaphore . WaitAsync ( 0 ) . ConfigureAwait ( false ) ) {
ASF . ArchiLogger . LogGenericDebug ( $"Skipped {nameof(SubmitData)} trigger because there is already one in progress." ) ;
return ;
}
try {
Dictionary < uint , ulong > appTokens = GlobalCache . GetAppTokensForSubmission ( ) ;
Dictionary < uint , ulong > packageTokens = GlobalCache . GetPackageTokensForSubmission ( ) ;
Dictionary < uint , string > depotKeys = GlobalCache . GetDepotKeysForSubmission ( ) ;
if ( ( appTokens . Count = = 0 ) & & ( packageTokens . Count = = 0 ) & & ( depotKeys . Count = = 0 ) ) {
ASF . ArchiLogger . LogGenericInfo ( "There is no new data to submit, everything up-to-date." ) ;
return ;
}
2020-08-22 19:41:01 +00:00
ulong contributorSteamID = ( ASF . GlobalConfig . SteamOwnerID > 0 ) & & new SteamID ( ASF . GlobalConfig . SteamOwnerID ) . IsIndividualAccount ? ASF . GlobalConfig . SteamOwnerID : Bot . Bots . Values . Where ( bot = > bot . SteamID > 0 ) . OrderByDescending ( bot = > bot . OwnedPackageIDsReadOnly . Count ) . FirstOrDefault ( ) ? . SteamID ? ? 0 ;
2020-06-13 10:08:21 +00:00
if ( contributorSteamID = = 0 ) {
ASF . ArchiLogger . LogGenericError ( $"Skipped {nameof(SubmitData)} trigger because there is no valid steamID we could classify as a contributor. Consider setting up {nameof(ASF.GlobalConfig.SteamOwnerID)} property." ) ;
return ;
}
2020-11-14 21:37:00 +00:00
RequestData requestData = new ( contributorSteamID , appTokens , packageTokens , depotKeys ) ;
2020-06-13 10:08:21 +00:00
ASF . ArchiLogger . LogGenericInfo ( $"Submitting registered apps/subs/depots: {appTokens.Count}/{packageTokens.Count}/{depotKeys.Count}..." ) ;
2020-09-13 19:43:25 +00:00
WebBrowser . ObjectResponse < ResponseData > ? response = await ASF . WebBrowser . UrlPostToJsonObject < ResponseData , RequestData > ( request , data : requestData , requestOptions : WebBrowser . ERequestOptions . ReturnClientErrors ) . ConfigureAwait ( false ) ;
2020-06-13 10:08:21 +00:00
2021-01-04 16:42:31 +00:00
if ( response = = null ) {
2020-06-13 10:08:21 +00:00
ASF . ArchiLogger . LogGenericWarning ( Strings . WarningFailed ) ;
2021-01-04 16:42:31 +00:00
return ;
}
if ( response . StatusCode . IsClientErrorCode ( ) ) {
ASF . ArchiLogger . LogGenericWarning ( string . Format ( CultureInfo . CurrentCulture , Strings . WarningFailedWithError , response . StatusCode ) ) ;
2020-06-13 10:08:21 +00:00
#if NETFRAMEWORK
2021-01-04 16:42:31 +00:00
if ( response . StatusCode = = ( HttpStatusCode ) 429 ) {
2020-06-13 10:08:21 +00:00
#else
2021-01-04 16:42:31 +00:00
if ( response . StatusCode = = HttpStatusCode . TooManyRequests ) {
2020-06-13 10:08:21 +00:00
#endif
TimeSpan startIn = TimeSpan . FromMinutes ( Utilities . RandomNext ( SharedInfo . MinimumMinutesBeforeFirstUpload , SharedInfo . MaximumMinutesBeforeFirstUpload ) ) ;
lock ( SubmissionTimer ) {
SubmissionTimer . Change ( startIn , TimeSpan . FromHours ( SharedInfo . MinimumHoursBetweenUploads ) ) ;
}
ASF . ArchiLogger . LogGenericInfo ( $"The submission will happen in approximately {startIn.ToHumanReadable()} from now." ) ;
}
return ;
}
ASF . ArchiLogger . LogGenericInfo ( $"Data successfully submitted. Newly registered apps/subs/depots: {response.Content.Data.NewAppsCount}/{response.Content.Data.NewSubsCount}/{response.Content.Data.NewDepotsCount}." ) ;
2021-01-03 21:02:24 +00:00
GlobalCache . UpdateSubmittedData ( appTokens , packageTokens , depotKeys ) ;
2020-06-13 10:08:21 +00:00
} finally {
SubmissionSemaphore . Release ( ) ;
}
}
}
}