diff --git a/SteamAccountDataFetcher/SteamAccountDataFetcher.csproj b/SteamAccountDataFetcher/SteamAccountDataFetcher.csproj index acb414a..a793b6b 100644 --- a/SteamAccountDataFetcher/SteamAccountDataFetcher.csproj +++ b/SteamAccountDataFetcher/SteamAccountDataFetcher.csproj @@ -9,7 +9,7 @@ Andrii Lavrenko Utility for collecting Steam account information Andrii Lavrenko - 3.0.1 + 3.0.2 diff --git a/SteamAccountDataFetcher/SteamDataClient/Client.cs b/SteamAccountDataFetcher/SteamDataClient/Client.cs index b8becee..e39add5 100644 --- a/SteamAccountDataFetcher/SteamDataClient/Client.cs +++ b/SteamAccountDataFetcher/SteamDataClient/Client.cs @@ -14,6 +14,7 @@ public class Client : IDisposable private SteamApps _steamApps; private bool _isRunning = false; + private bool _isAuthUnsuccess = false; private bool _isLicensesProcessed = false; private bool _isWebAPIProcessed = false; @@ -31,6 +32,7 @@ internal SteamID? SteamID internal string RefreshToken { get; private set; } = string.Empty; private static uint _instance = 0; + private static bool _rateLimitReached = false; private static DateTime _lastConnectionTime = DateTime.MinValue; private static List? _packagesInfo = null; @@ -94,13 +96,7 @@ internal async Task GetResponseDataAsync() internal async Task RunAsync() { - var secondsBetweenLastConnect = DateTime.Now - _lastConnectionTime; - if (secondsBetweenLastConnect < Configuration.DefaultLoginTimeout) - { - var timeToWait = Configuration.DefaultLoginTimeout - secondsBetweenLastConnect; - Log($"Last connection was {secondsBetweenLastConnect.Seconds} seconds ago, wait {timeToWait.Seconds} seconds."); - await Task.Delay(timeToWait); - } + await WaitOrProceed(); _isRunning = true; _steamClient.Connect(); @@ -117,33 +113,38 @@ internal async Task RunAsync() private async Task LoginAsync() { - while (true) + try { - try + CredentialsAuthSession authSession = await _steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new() { - CredentialsAuthSession authSession = await _steamClient.Authentication.BeginAuthSessionViaCredentialsAsync(new() - { - Username = Username, - Password = Password, - Authenticator = _autoTwoFactorAuthenticator - }); - AuthPollResult authPollResult = await authSession.PollingWaitForResultAsync(); - AccessToken = authPollResult.AccessToken; - RefreshToken = authPollResult.RefreshToken; - break; - } - catch (AuthenticationException e) + Username = Username, + Password = Password, + Authenticator = _autoTwoFactorAuthenticator + }); + AuthPollResult authPollResult = await authSession.PollingWaitForResultAsync(); + AccessToken = authPollResult.AccessToken; + RefreshToken = authPollResult.RefreshToken; + } + catch (AuthenticationException e) + { + if (e.Result == EResult.AccountLoginDeniedThrottle || e.Result == EResult.RateLimitExceeded) { - Log($"Unable to authenticate user to Steam Client with error {e.Message}.", Logger.Level.Error); + Log("RateLimit reached.", Logger.Level.Warning); + _rateLimitReached = true; _steamClient.Disconnect(); return; } - catch (TaskCanceledException) - { - Log("Failure to authenticate user to Steam Client. Retrying...", Logger.Level.Warning); - await Task.Delay(1000); - continue; - } + + Log($"Unable to authenticate user to Steam Client with error {e.Message}.", Logger.Level.Error); + _steamClient.Disconnect(); + return; + } + catch (TaskCanceledException) + { + Log("Failure to authenticate user to Steam Client. Retrying...", Logger.Level.Warning); + _isAuthUnsuccess = true; + _steamClient.Disconnect(); + return; } _steamUser.LogOn(new() @@ -178,7 +179,7 @@ private async void OnConnectedAsync(SteamClient.ConnectedCallback callback) private async void OnDisconnectedAsync(SteamClient.DisconnectedCallback callback) { - if (callback.UserInitiated) + if (callback.UserInitiated && !_rateLimitReached && !_isAuthUnsuccess) { Log("Disconnected from Steam Network by user."); @@ -186,14 +187,10 @@ private async void OnDisconnectedAsync(SteamClient.DisconnectedCallback callback return; } + _isAuthUnsuccess = false; Log("Disconnected from Steam Network.", Logger.Level.Error); - var secondsBetweenLastConnect = DateTime.Now - _lastConnectionTime; - if (secondsBetweenLastConnect < Configuration.DefaultLoginTimeout) - { - var timeToWait = Configuration.DefaultLoginTimeout - secondsBetweenLastConnect; - Log($"Last connection was {secondsBetweenLastConnect.Seconds} seconds ago, wait {timeToWait.Seconds} seconds."); - await Task.Delay(timeToWait); - } + await WaitOrProceed(); + _steamClient.Connect(); } @@ -349,6 +346,27 @@ private async void OnLicenseListAsync(SteamApps.LicenseListCallback callback) _isLicensesProcessed = true; } + private async Task WaitOrProceed() + { + if (_lastConnectionTime == DateTime.MinValue) + return; + + TimeSpan deltaBetweenLastConnect = DateTime.Now - _lastConnectionTime; + TimeSpan timeToWait = Configuration.DefaultLoginTimeout - deltaBetweenLastConnect; + + if (_rateLimitReached) + { + _rateLimitReached = false; + timeToWait += Configuration.DefaultRateLimitTimeout; + } + + if (timeToWait.TotalSeconds > 1) + { + Log($"Waiting {timeToWait.TotalSeconds:N2} seconds."); + await Task.Delay(timeToWait); + } + } + public void Dispose() => _steamWebClient?.Dispose(); internal void Log(string message, Logger.Level level = Logger.Level.Info, [CallerMemberName] string callerName = "") => diff --git a/SteamAccountDataFetcher/SteamDataClient/Configuration.cs b/SteamAccountDataFetcher/SteamDataClient/Configuration.cs index 75b6e00..db43dc0 100644 --- a/SteamAccountDataFetcher/SteamDataClient/Configuration.cs +++ b/SteamAccountDataFetcher/SteamDataClient/Configuration.cs @@ -11,6 +11,7 @@ internal static class Configuration internal static string ReadCSVFilePath { get; } = "SteamAccountsLogin.txt"; internal static string WriteJSONFilePath { get; } = "SteamAccounts.json"; internal static string ApiDomain { get; } = "serious-re.com"; //"sadf.localhost"; - internal static TimeSpan DefaultLoginTimeout { get; } = TimeSpan.FromSeconds(20); - internal static TimeSpan DefaultWebRequestTimeout { get; } = TimeSpan.FromSeconds(1); + internal static TimeSpan DefaultLoginTimeout { get; } = TimeSpan.FromSeconds(30); + internal static TimeSpan DefaultRateLimitTimeout { get; } = TimeSpan.FromMinutes(30); + internal static TimeSpan DefaultWebRequestTimeout { get; } = TimeSpan.FromSeconds(3); } diff --git a/SteamAccountDataFetcher/SteamDataClient/SteamWebClient.cs b/SteamAccountDataFetcher/SteamDataClient/SteamWebClient.cs index d82fec4..e1f6a6b 100644 --- a/SteamAccountDataFetcher/SteamDataClient/SteamWebClient.cs +++ b/SteamAccountDataFetcher/SteamDataClient/SteamWebClient.cs @@ -18,7 +18,7 @@ internal class SteamWebClient: IDisposable private SemaphoreSlim _webAPISemaphore = new(1); internal string SessionID { get; private set; } = string.Empty; - private static DateTime _lastRequestTime = DateTime.MinValue; + private static bool _isFirstRequestBefore = false; internal SteamWebClient(Client steamClient) { @@ -223,10 +223,12 @@ internal void InitAsync() private async Task RunOrSleep() { - while (DateTime.Now - _lastRequestTime < Configuration.DefaultWebRequestTimeout) + if (_isFirstRequestBefore) + { await Task.Delay(Configuration.DefaultWebRequestTimeout); - - _lastRequestTime = DateTime.Now; + return; + } + _isFirstRequestBefore = true; } public void Dispose()