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);