diff --git a/ArchiSteamFarm/Steam/Bot.cs b/ArchiSteamFarm/Steam/Bot.cs index c2d93539d..253d47dc4 100644 --- a/ArchiSteamFarm/Steam/Bot.cs +++ b/ArchiSteamFarm/Steam/Bot.cs @@ -2437,7 +2437,6 @@ public sealed class Bot : IAsyncDisposable, IDisposable { ); } - ArchiWebHandler.OnInitModules(); BotDatabase.MobileAuthenticator?.OnInitModules(); await PluginsCore.OnBotInitModules(this, BotConfig.AdditionalProperties).ConfigureAwait(false); diff --git a/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs b/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs index 4680584e5..c2e7f3e35 100644 --- a/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs +++ b/ArchiSteamFarm/Steam/Cards/CardsFarmer.cs @@ -37,7 +37,6 @@ using AngleSharp.Dom; using AngleSharp.XPath; using ArchiSteamFarm.Collections; using ArchiSteamFarm.Core; -using ArchiSteamFarm.Helpers; using ArchiSteamFarm.Localization; using ArchiSteamFarm.Plugins; using ArchiSteamFarm.Steam.Data; @@ -1177,7 +1176,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable { GamesToFarm.Clear(); - (_, FrozenSet? privateAppIDs) = await Bot.ArchiWebHandler.CachedPrivateAppIDs.GetValue(ECacheFallback.SuccessPreviously).ConfigureAwait(false); + HashSet? privateAppIDs = await Bot.ArchiHandler.GetPrivateAppIDs().ConfigureAwait(false); ConcurrentHashSet parsedAppIDs = []; @@ -1276,7 +1275,7 @@ public sealed class CardsFarmer : IAsyncDisposable, IDisposable { // Lastly, we forcefully apply random order to those considered the same in value, as we can't really afford massive amount of misses in a row Dictionary gamesToFarm = boosterCreatorEntries.Where(entry => !boosterElibility.Contains(entry.AppID) && (!Bot.BotDatabase.FarmingRiskyIgnoredAppIDs.TryGetValue(entry.AppID, out DateTime ignoredUntil) || (ignoredUntil < now)) && ShouldIdle(entry.AppID)).ToDictionary(static entry => entry.AppID, static entry => entry.Name); - (_, FrozenSet? privateAppIDs) = await Bot.ArchiWebHandler.CachedPrivateAppIDs.GetValue(ECacheFallback.SuccessPreviously).ConfigureAwait(false); + HashSet? privateAppIDs = await Bot.ArchiHandler.GetPrivateAppIDs().ConfigureAwait(false); if (privateAppIDs != null) { foreach (uint appID in privateAppIDs) { diff --git a/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs index 27438a4dc..589da7dd1 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiHandler.cs @@ -36,6 +36,16 @@ using ArchiSteamFarm.Steam.Integration.CMsgs; using JetBrains.Annotations; using SteamKit2; using SteamKit2.Internal; +using SteamKit2.WebUI.Internal; +using CMsgClientChangeStatus = SteamKit2.Internal.CMsgClientChangeStatus; +using CMsgClientCommentNotifications = SteamKit2.Internal.CMsgClientCommentNotifications; +using CMsgClientGamesPlayed = SteamKit2.Internal.CMsgClientGamesPlayed; +using CMsgClientItemAnnouncements = SteamKit2.Internal.CMsgClientItemAnnouncements; +using CMsgClientRedeemGuestPass = SteamKit2.Internal.CMsgClientRedeemGuestPass; +using CMsgClientRequestItemAnnouncements = SteamKit2.Internal.CMsgClientRequestItemAnnouncements; +using CMsgClientSharedLibraryLockStatus = SteamKit2.Internal.CMsgClientSharedLibraryLockStatus; +using CMsgClientUIMode = SteamKit2.Internal.CMsgClientUIMode; +using CMsgClientUserNotifications = SteamKit2.Internal.CMsgClientUserNotifications; using EPersonaStateFlag = SteamKit2.EPersonaStateFlag; namespace ArchiSteamFarm.Steam.Integration; @@ -44,6 +54,8 @@ public sealed class ArchiHandler : ClientMsgHandler { internal const byte MaxGamesPlayedConcurrently = 32; // This is limit introduced by Steam Network private readonly ArchiLogger ArchiLogger; + + private readonly SteamUnifiedMessages.UnifiedService UnifiedAccountPrivateApps; private readonly SteamUnifiedMessages.UnifiedService UnifiedChatRoomService; private readonly SteamUnifiedMessages.UnifiedService UnifiedClanChatRoomsService; private readonly SteamUnifiedMessages.UnifiedService UnifiedCredentialsService; @@ -60,6 +72,8 @@ public sealed class ArchiHandler : ClientMsgHandler { ArgumentNullException.ThrowIfNull(steamUnifiedMessages); ArchiLogger = archiLogger; + + UnifiedAccountPrivateApps = steamUnifiedMessages.CreateService(); UnifiedChatRoomService = steamUnifiedMessages.CreateService(); UnifiedClanChatRoomsService = steamUnifiedMessages.CreateService(); UnifiedCredentialsService = steamUnifiedMessages.CreateService(); @@ -253,6 +267,36 @@ public sealed class ArchiHandler : ClientMsgHandler { } } + internal async Task?> GetPrivateAppIDs() { + if (Client == null) { + throw new InvalidOperationException(nameof(Client)); + } + + if (!Client.IsConnected) { + return null; + } + + CAccountPrivateApps_GetPrivateAppList_Request request = new(); + + SteamUnifiedMessages.ServiceMethodResponse response; + + try { + response = await UnifiedAccountPrivateApps.SendMessage(x => x.GetPrivateAppList(request)).ToLongRunningTask().ConfigureAwait(false); + } catch (Exception e) { + ArchiLogger.LogGenericWarningException(e); + + return null; + } + + if (response.Result != EResult.OK) { + return null; + } + + CAccountPrivateApps_GetPrivateAppList_Response body = response.GetDeserializedResponse(); + + return body.private_apps.appids.Select(static appID => (uint) appID).ToHashSet(); + } + [PublicAPI] public async Task?> GetOwnedGames(ulong steamID) { if ((steamID == 0) || !new SteamID(steamID).IsIndividualAccount) { diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index 4b04382c3..3c00af466 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -22,7 +22,6 @@ // limitations under the License. using System; -using System.Collections.Frozen; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; @@ -52,7 +51,6 @@ using SteamKit2; namespace ArchiSteamFarm.Steam.Integration; public sealed class ArchiWebHandler : IDisposable { - private const string AccountPrivateAppsService = "IAccountPrivateAppsService"; private const string EconService = "IEconService"; private const string LoyaltyRewardsService = "ILoyaltyRewardsService"; private const ushort MaxItemsInSingleInventoryRequest = 5000; @@ -76,8 +74,6 @@ public sealed class ArchiWebHandler : IDisposable { [PublicAPI] public WebBrowser WebBrowser { get; } - internal readonly ArchiCacheable> CachedPrivateAppIDs; - private readonly Bot Bot; private readonly SemaphoreSlim SessionSemaphore = new(1, 1); @@ -91,12 +87,10 @@ public sealed class ArchiWebHandler : IDisposable { ArgumentNullException.ThrowIfNull(bot); Bot = bot; - CachedPrivateAppIDs = new ArchiCacheable>(ResolvePrivateAppIDs, TimeSpan.FromMinutes(5)); WebBrowser = new WebBrowser(bot.ArchiLogger, ASF.GlobalConfig?.WebProxy); } public void Dispose() { - CachedPrivateAppIDs.Dispose(); SessionSemaphore.Dispose(); WebBrowser.Dispose(); } @@ -2123,8 +2117,6 @@ public sealed class ArchiWebHandler : IDisposable { internal void OnDisconnected() => Initialized = false; - internal void OnInitModules() => Utilities.InBackground(() => CachedPrivateAppIDs.Reset()); - internal void OnVanityURLChanged(string? vanityURL = null) => VanityURL = !string.IsNullOrEmpty(vanityURL) ? vanityURL : null; internal async Task<(EResult Result, EPurchaseResultDetail? PurchaseResult, string? BalanceText)?> RedeemWalletKey(string key) { @@ -2294,69 +2286,6 @@ public sealed class ArchiWebHandler : IDisposable { } } - private async Task<(bool Success, FrozenSet? Result)> ResolvePrivateAppIDs(CancellationToken cancellationToken) { - string? accessToken = Bot.AccessToken; - - if (string.IsNullOrEmpty(accessToken)) { - return (false, null); - } - - Dictionary arguments = new(1, StringComparer.Ordinal) { - { "access_token", accessToken } - }; - - KeyValue? response = null; - - for (byte i = 0; (i < WebBrowser.MaxTries) && (response == null); i++) { - if ((i > 0) && (WebLimiterDelay > 0)) { - await Task.Delay(WebLimiterDelay, cancellationToken).ConfigureAwait(false); - } - - using WebAPI.AsyncInterface accountPrivateAppsService = Bot.SteamConfiguration.GetAsyncWebAPIInterface(AccountPrivateAppsService); - - accountPrivateAppsService.Timeout = WebBrowser.Timeout; - - try { - response = await WebLimitRequest( - WebAPI.DefaultBaseAddress, - - // ReSharper disable once AccessToDisposedClosure - async () => await accountPrivateAppsService.CallAsync(HttpMethod.Get, "GetPrivateAppList", args: arguments).ConfigureAwait(false), cancellationToken - ).ConfigureAwait(false); - } catch (TaskCanceledException e) { - Bot.ArchiLogger.LogGenericDebuggingException(e); - } catch (Exception e) { - Bot.ArchiLogger.LogGenericWarningException(e); - } - } - - if (response == null) { - Bot.ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.ErrorRequestFailedTooManyTimes, WebBrowser.MaxTries)); - - return (false, null); - } - - List nodes = response["private_apps"]["appids"].Children; - - if (nodes.Count == 0) { - return (true, FrozenSet.Empty); - } - - HashSet result = new(nodes.Count); - - foreach (uint appID in nodes.Select(static node => node.AsUnsignedInteger())) { - if (appID == 0) { - Bot.ArchiLogger.LogNullError(appID); - - return (false, null); - } - - result.Add(appID); - } - - return (true, result.ToFrozenSet()); - } - private static void SetDescriptionsToAssets(IEnumerable assets, [SuppressMessage("ReSharper", "SuggestBaseTypeForParameter")] Dictionary<(uint AppID, ulong ClassID, ulong InstanceID), InventoryDescription> descriptions) { ArgumentNullException.ThrowIfNull(assets); ArgumentNullException.ThrowIfNull(descriptions); diff --git a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs index 9413d6a23..77af12465 100644 --- a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs +++ b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs @@ -44,7 +44,8 @@ namespace ArchiSteamFarm.Steam.Security; public sealed class MobileAuthenticator : IDisposable { internal const byte BackupCodeDigits = 7; internal const byte CodeDigits = 5; - internal const byte CodeInterval = 30; + + private const byte CodeInterval = 30; // For how many minutes we can assume that SteamTimeDifference is correct private const byte SteamTimeTTL = 15;