diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs index 4103abf5..de4e7f66 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs @@ -137,7 +137,7 @@ public class User : ObservableObject .ConfigureAwait(false); UserGameRoles = await userGameRoleClient - .GetUserGameRolesAsync(this, token) + .GetUserGameRolesAsync(Entity, token) .ConfigureAwait(false); SelectedUserGameRole = UserGameRoles.FirstOrFirstOrDefault(role => role.IsChosen); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/ApiInformationAttribute.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/ApiInformationAttribute.cs index 2f0f58f5..7be30889 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/ApiInformationAttribute.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Annotation/ApiInformationAttribute.cs @@ -2,11 +2,6 @@ // Licensed under the MIT license. using Snap.Hutao.Web.Hoyolab.DynamicSecret; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Snap.Hutao.Web.Hoyolab.Annotation; @@ -15,7 +10,7 @@ namespace Snap.Hutao.Web.Hoyolab.Annotation; /// /// API 的返回类型 [AttributeUsage(AttributeTargets.Method)] -internal class ApiInformationAttribute : Attribute +internal class ApiInformationAttribute : Attribute { /// /// Cookie类型 diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs index 8595699c..e3338c56 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/ApiEndpoints.cs @@ -1,6 +1,8 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using System.ComponentModel.DataAnnotations; + namespace Snap.Hutao.Web.Hoyolab; /// @@ -105,7 +107,17 @@ internal static class ApiEndpoints /// /// 用户游戏角色 /// - public const string UserGameRoles = $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?game_biz=hk4e_cn"; + /// 操作凭证 + /// 用户游戏角色字符串 + public static string UserGameRolesByActionTicket(string actionTicket) + { + return $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?action_ticket={actionTicket}&game_biz=hk4e_cn"; + } + + /// + /// 用户游戏角色 + /// + public const string UserGameRolesByCookie = $"{ApiTaKumiBindingApi}/getUserGameRolesByCookie?game_biz=hk4e_cn"; /// /// AuthKey @@ -115,6 +127,18 @@ internal static class ApiEndpoints #region Auth + /// + /// 获取 stoken 与 ltoken + /// + /// 操作类型 game_role + /// Stoken + /// uid + /// Url + public static string AuthActionTicket(string actionType, string stoken, string uid) + { + return $"{ApiTakumiAuthApi}/getMultiTokenByLoginTicket?action_type={actionType}&stoken={Uri.EscapeDataString(stoken)}&uid={uid}"; + } + /// /// 获取 stoken 与 ltoken /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/GameRecord/CardClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/GameRecord/CardClient.cs index dced6c50..b0ed0226 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/GameRecord/CardClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/GameRecord/CardClient.cs @@ -39,7 +39,7 @@ internal class CardClient /// 用户 /// 取消令牌 /// 桌面小组件数据 - [ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.X6)] + [ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.X6)] public async Task GetWidgetDataAsync(User user, CancellationToken token) { Response>? resp = await httpClient diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieType.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieType.cs index 35ff67a0..fd0332df 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieType.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieType.cs @@ -13,6 +13,11 @@ public enum CookieType /// None, + /// + /// 需要 Ltoken 与 e_hk4e_token + /// + Ltoken, + /// /// 需要 Stoken /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs index 95d01174..dc398b87 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs @@ -38,7 +38,7 @@ internal class PassportClient /// 用户 /// 取消令牌 /// 验证信息 - [ApiInformation(Cookie = CookieType.All)] + [ApiInformation(Cookie = CookieType.All)] public async Task VerifyLtokenAsync(User user, CancellationToken token) { Response? response = await httpClient diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AccountInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AccountInfo.cs new file mode 100644 index 00000000..3b8bf8be --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AccountInfo.cs @@ -0,0 +1,92 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; +using Snap.Hutao.Web.Response; +using System.Net.Http; + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth; + +/// +/// 账户信息 +/// +[SuppressMessage("", "SA1600")] +public class AccountInfo +{ + + [JsonPropertyName("is_realname")] + public bool IsRealname { get; set; } + + [JsonPropertyName("mobile")] + public string Mobile { get; set; } = default!; + + [JsonPropertyName("safe_mobile")] + public string SafeMobile { get; set; } = default!; + + [JsonPropertyName("account_id")] + public string AccountId { get; set; } = default!; + + [JsonPropertyName("account_name")] + public string AccountName { get; set; } = default!; + + [JsonPropertyName("email")] + public string Email { get; set; } = default!; + + [JsonPropertyName("is_email_verify")] + public bool IsEmailVerify { get; set; } + + [JsonPropertyName("area_code")] + public string AreaCode { get; set; } = default!; + + [JsonPropertyName("safe_area_code")] + public string SafeAreaCode { get; set; } = default!; + + [JsonPropertyName("real_name")] + public string RealName { get; set; } = default!; + + [JsonPropertyName("identity_code")] + public string IdentityCode { get; set; } = default!; + + [JsonPropertyName("create_time")] + public string CreateTime { get; set; } = default!; + + [JsonPropertyName("create_ip")] + public string CreateIp { get; set; } = default!; + + [JsonPropertyName("change_pwd_time")] + public string ChangePwdTime { get; set; } = default!; + + [JsonPropertyName("nickname")] + public string Nickname { get; set; } = default!; + + [JsonPropertyName("user_icon_id")] + public int UserIconId { get; set; } + + [JsonPropertyName("safe_level")] + public int SafeLevel { get; set; } + + [JsonPropertyName("black_endtime")] + public string BlackEndtime { get; set; } = default!; + + [JsonPropertyName("black_note")] + public string BlackNote { get; set; } = default!; + + [JsonPropertyName("gender")] + public int Gender { get; set; } + + [JsonPropertyName("real_stat")] + public int RealStat { get; set; } + + [JsonPropertyName("apple_name")] + public string AppleName { get; set; } = default!; + + [JsonPropertyName("sony_name")] + public string SonyName { get; set; } = default!; + + [JsonPropertyName("tap_name")] + public string TapName { get; set; } = default!; + + [JsonPropertyName("reactivate_ticket")] + public string ReactivateTicket { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs new file mode 100644 index 00000000..6147751a --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs @@ -0,0 +1,33 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Web.Hoyolab.Takumi.Binding; +using Snap.Hutao.Web.Response; +using System.Net.Http; + +namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth; + +/// +/// 操作凭证包装器 +/// +public class ActionTicketWrapper +{ + /// + /// 凭证 + /// + [JsonPropertyName("ticket")] + public string Ticket { get; set; } + + /// + /// 是否验证 + /// + [JsonPropertyName("is_verified")] + public bool IsVerified { get; set; } + + /// + /// 账户信息 + /// + [JsonPropertyName("account_info")] + public AccountInfo AccountInfo { get; set; } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs index 2452c52e..e756c7c8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Model.Entity; +using Snap.Hutao.Web.Hoyolab.Annotation; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using Snap.Hutao.Web.Response; using System.Net.Http; @@ -31,6 +33,30 @@ internal class AuthClient this.logger = logger; } + /// + /// 异步获取操作凭证 + /// + /// 操作 + /// 用户 + /// 操作凭证 + [ApiInformation(Cookie = CookieType.Stoken, Salt = DynamicSecret.SaltType.K2)] + public async Task GetActionTicketByStokenAsync(string action, User user) + { + if (user.Cookie!.TryGetValue(Cookie.STOKEN, out string? stoken)) + { + if (user.Cookie.TryGetUid(out string? uid)) + { + Response? resp = await httpClient + .TryCatchGetFromJsonAsync>(ApiEndpoints.AuthActionTicket(action, stoken, uid), options, logger) + .ConfigureAwait(false); + + return resp.Data?.Ticket; + } + } + + return null; + } + /// /// 获取 MultiToken /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs index c8414a61..2b2c202e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs @@ -4,6 +4,7 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Extension; using Snap.Hutao.Model.Binding.User; +using Snap.Hutao.Web.Hoyolab.Annotation; using Snap.Hutao.Web.Response; using System.Net.Http; @@ -35,12 +36,21 @@ internal class BindingClient /// /// 获取用户角色信息 /// + /// 操作凭证 /// 用户 /// 取消令牌 /// 用户角色信息 - public Task> GetUserGameRolesAsync(User user, CancellationToken token = default) + [ApiInformation(Cookie = CookieType.Ltoken)] + public async Task> GetUserGameRolesAsync(string actionTicket, Model.Entity.User user, CancellationToken token = default) { - return GetUserGameRolesAsync(user.Entity, token); + string url = ApiEndpoints.UserGameRolesByActionTicket(actionTicket); + + Response>? resp = await httpClient + .SetUser(user) + .TryCatchGetFromJsonAsync>>(url, options, logger, token) + .ConfigureAwait(false); + + return EnumerableExtension.EmptyIfNull(resp?.Data?.List); } /// @@ -53,7 +63,7 @@ internal class BindingClient { Response>? resp = await httpClient .SetUser(user) - .TryCatchGetFromJsonAsync>>(ApiEndpoints.UserGameRoles, options, logger, token) + .TryCatchGetFromJsonAsync>>(ApiEndpoints.UserGameRolesByCookie, options, logger, token) .ConfigureAwait(false); return EnumerableExtension.EmptyIfNull(resp?.Data?.List);