diff --git a/ArchiSteamFarm/Steam/Data/ConfirmationData.cs b/ArchiSteamFarm/Steam/Data/ConfirmationData.cs new file mode 100644 index 000000000..d631e8677 --- /dev/null +++ b/ArchiSteamFarm/Steam/Data/ConfirmationData.cs @@ -0,0 +1,38 @@ +// _ _ _ ____ _ _____ +// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ +// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ +// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| +// | +// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki +// 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 Newtonsoft.Json; + +namespace ArchiSteamFarm.Steam.Data; + +internal sealed class ConfirmationData { + [JsonProperty(PropertyName = "id", Required = Required.Always)] + internal readonly ulong ID; + + [JsonProperty(PropertyName = "nonce", Required = Required.Always)] + internal readonly ulong Nonce; + + [JsonProperty(PropertyName = "creator_id", Required = Required.Always)] + internal readonly ulong CreatorID; + + [JsonProperty(PropertyName = "type", Required = Required.Always)] + internal readonly string TypeText = ""; +} diff --git a/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs b/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs new file mode 100644 index 000000000..86f096b47 --- /dev/null +++ b/ArchiSteamFarm/Steam/Data/ConfirmationsResponse.cs @@ -0,0 +1,36 @@ +// _ _ _ ____ _ _____ +// / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ +// / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ +// / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +// /_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| +// | +// Copyright 2015-2023 Łukasz "JustArchi" Domeradzki +// 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.Collections.Generic; +using Newtonsoft.Json; + +namespace ArchiSteamFarm.Steam.Data; + +internal sealed class ConfirmationsResponse : BooleanResponse { + [JsonProperty("needauth", Required = Required.DisallowNull)] + internal readonly bool NeedAuthentication; + + [JsonProperty("conf", Required = Required.DisallowNull)] + internal readonly HashSet? Confirmations; + + [JsonConstructor] + private ConfirmationsResponse() { } +} diff --git a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs index 3fbd3d96b..d4b00251f 100644 --- a/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs +++ b/ArchiSteamFarm/Steam/Integration/ArchiWebHandler.cs @@ -1853,7 +1853,7 @@ public sealed class ArchiWebHandler : IDisposable { return resultInSeconds == 0 ? (byte) 0 : (byte) (resultInSeconds / 86400); } - internal async Task GetConfirmationsPage(string deviceID, string confirmationHash, ulong time) { + internal async Task GetConfirmationsPage(string deviceID, string confirmationHash, ulong time) { if (string.IsNullOrEmpty(deviceID)) { throw new ArgumentNullException(nameof(deviceID)); } @@ -1889,9 +1889,9 @@ public sealed class ArchiWebHandler : IDisposable { } } - Uri request = new(SteamCommunityURL, $"/mobileconf/conf?a={Bot.SteamID}&k={Uri.EscapeDataString(confirmationHash)}&l=english&m=android&p={Uri.EscapeDataString(deviceID)}&t={time}&tag=conf"); + Uri request = new(SteamCommunityURL, $"/mobileconf/getlist?a={Bot.SteamID}&k={Uri.EscapeDataString(confirmationHash)}&l=english&m=react&p={Uri.EscapeDataString(deviceID)}&t={time}&tag=conf"); - HtmlDocumentResponse? response = await UrlGetToHtmlDocumentWithSession(request, checkSessionPreemptively: false).ConfigureAwait(false); + ObjectResponse? response = await UrlGetToJsonObjectWithSession(request, checkSessionPreemptively: false).ConfigureAwait(false); return response?.Content; } @@ -2121,7 +2121,7 @@ public sealed class ArchiWebHandler : IDisposable { } } - Uri request = new(SteamCommunityURL, $"/mobileconf/ajaxop?a={Bot.SteamID}&cid={confirmationID}&ck={confirmationKey}&k={Uri.EscapeDataString(confirmationHash)}&l=english&m=android&op={(accept ? "allow" : "cancel")}&p={Uri.EscapeDataString(deviceID)}&t={time}&tag=conf"); + Uri request = new(SteamCommunityURL, $"/mobileconf/ajaxop?a={Bot.SteamID}&cid={confirmationID}&ck={confirmationKey}&k={Uri.EscapeDataString(confirmationHash)}&l=english&m=react&op={(accept ? "allow" : "cancel")}&p={Uri.EscapeDataString(deviceID)}&t={time}&tag=conf"); ObjectResponse? response = await UrlGetToJsonObjectWithSession(request).ConfigureAwait(false); @@ -2165,7 +2165,7 @@ public sealed class ArchiWebHandler : IDisposable { List> data = new(8 + (confirmations.Count * 2)) { new KeyValuePair("a", Bot.SteamID.ToString(CultureInfo.InvariantCulture)), new KeyValuePair("k", confirmationHash), - new KeyValuePair("m", "android"), + new KeyValuePair("m", "react"), new KeyValuePair("op", accept ? "allow" : "cancel"), new KeyValuePair("p", deviceID), new KeyValuePair("t", time.ToString(CultureInfo.InvariantCulture)), diff --git a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs index e4594760d..d83dbe4d6 100644 --- a/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs +++ b/ArchiSteamFarm/Steam/Security/MobileAuthenticator.cs @@ -28,10 +28,10 @@ using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; -using AngleSharp.Dom; using ArchiSteamFarm.Core; using ArchiSteamFarm.Helpers; using ArchiSteamFarm.Localization; +using ArchiSteamFarm.Steam.Data; using ArchiSteamFarm.Storage; using Newtonsoft.Json; @@ -172,71 +172,23 @@ public sealed class MobileAuthenticator : IDisposable { } // ReSharper disable RedundantSuppressNullableWarningExpression - required for .NET Framework - using IDocument? htmlDocument = await Bot.ArchiWebHandler.GetConfirmationsPage(deviceID!, confirmationHash!, time).ConfigureAwait(false); + ConfirmationsResponse? response = await Bot.ArchiWebHandler.GetConfirmationsPage(deviceID!, confirmationHash!, time).ConfigureAwait(false); // ReSharper restore RedundantSuppressNullableWarningExpression - required for .NET Framework - if (htmlDocument == null) { + if (response?.Confirmations == null) { return null; } - IEnumerable confirmationNodes = htmlDocument.SelectNodes("//div[@class='mobileconf_list_entry']"); + if (!response.Success) { + return null; + } HashSet result = new(); - foreach (IElement confirmationNode in confirmationNodes) { - string? idText = confirmationNode.GetAttribute("data-confid"); - - if (string.IsNullOrEmpty(idText)) { - Bot.ArchiLogger.LogNullError(idText); - - return null; - } - - if (!ulong.TryParse(idText, out ulong id) || (id == 0)) { - Bot.ArchiLogger.LogNullError(id); - - return null; - } - - string? keyText = confirmationNode.GetAttribute("data-key"); - - if (string.IsNullOrEmpty(keyText)) { - Bot.ArchiLogger.LogNullError(keyText); - - return null; - } - - if (!ulong.TryParse(keyText, out ulong key) || (key == 0)) { - Bot.ArchiLogger.LogNullError(key); - - return null; - } - - string? creatorText = confirmationNode.GetAttribute("data-creator"); - - if (string.IsNullOrEmpty(creatorText)) { - Bot.ArchiLogger.LogNullError(creatorText); - - return null; - } - - if (!ulong.TryParse(creatorText, out ulong creator) || (creator == 0)) { - Bot.ArchiLogger.LogNullError(creator); - - return null; - } - - string? typeText = confirmationNode.GetAttribute("data-type"); - - if (string.IsNullOrEmpty(typeText)) { - Bot.ArchiLogger.LogNullError(typeText); - - return null; - } - + foreach (ConfirmationData confirmation in response.Confirmations) { // ReSharper disable once RedundantSuppressNullableWarningExpression - required for .NET Framework - if (!Enum.TryParse(typeText!, out Confirmation.EType type) || (type == Confirmation.EType.Unknown)) { + if (!Enum.TryParse(confirmation.TypeText!, out Confirmation.EType type) || (type == Confirmation.EType.Unknown)) { Bot.ArchiLogger.LogNullError(type); return null; @@ -248,7 +200,7 @@ public sealed class MobileAuthenticator : IDisposable { return null; } - result.Add(new Confirmation(id, key, creator, type)); + result.Add(new Confirmation(confirmation.ID, confirmation.Nonce, confirmation.CreatorID, type)); } return result;