mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support verifyLtoken for #207
This commit is contained in:
@@ -17,14 +17,24 @@ internal static class CoreEnvironment
|
||||
// 计算过程:https://github.com/UIGF-org/Hoyolab.Salt
|
||||
|
||||
/// <summary>
|
||||
/// 动态密钥1的盐
|
||||
/// 动态密钥1的K2盐
|
||||
/// </summary>
|
||||
public const string DynamicSecret1Salt = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a";
|
||||
public const string DynamicSecretK2Salt = "fdv0fY9My9eA7MR0NpjGP9RjueFvjUSQ";
|
||||
|
||||
/// <summary>
|
||||
/// 动态密钥2的盐
|
||||
/// 动态密钥1的LK2盐
|
||||
/// </summary>
|
||||
public const string DynamicSecret2Salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs";
|
||||
public const string DynamicSecretLK2Salt = "jEpJb9rRARU2rXDA9qYbZ3selxkuct9a";
|
||||
|
||||
/// <summary>
|
||||
/// 动态密钥2的X4盐
|
||||
/// </summary>
|
||||
public const string DynamicSecretX4Salt = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs";
|
||||
|
||||
/// <summary>
|
||||
/// 动态密钥2的X6盐
|
||||
/// </summary>
|
||||
public const string DynamicSecretX6Salt = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v";
|
||||
|
||||
/// <summary>
|
||||
/// 米游社请求UA
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -20,4 +21,9 @@ internal class ApiInformationAttribute<TReturnType> : Attribute
|
||||
/// Cookie类型
|
||||
/// </summary>
|
||||
public CookieType Cookie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SALT
|
||||
/// </summary>
|
||||
public SaltType Salt { get; set; }
|
||||
}
|
||||
@@ -145,6 +145,14 @@ internal static class ApiEndpoints
|
||||
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18
|
||||
#endregion
|
||||
|
||||
#region ma-cn-session
|
||||
|
||||
/// <summary>
|
||||
/// 验证 Ltoken 有效性
|
||||
/// </summary>
|
||||
public const string AccountVerifyLtoken = $"{PassportApiV4}/account/ma-cn-session/web/verifyLtoken";
|
||||
#endregion
|
||||
|
||||
#region App
|
||||
|
||||
/// <summary>
|
||||
@@ -171,6 +179,8 @@ internal static class ApiEndpoints
|
||||
private const string Hk4eApiAnnouncementApi = $"{Hk4eApi}/common/hk4e_cn/announcement/api";
|
||||
private const string Hk4eApiGachaInfoApi = $"{Hk4eApi}/event/gacha_info/api";
|
||||
|
||||
private const string PassportApiV4 = "passport-api-v4.mihoyo.com";
|
||||
|
||||
private const string SdkStatic = "https://sdk-static.mihoyo.com";
|
||||
private const string SdkStaticLauncherApi = $"{SdkStatic}/hk4e_cn/mdk/launcher/api";
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Web.Hoyolab.Annotation;
|
||||
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
@@ -23,7 +24,7 @@ internal class CardClient
|
||||
/// 构造一个新的桌面卡片客户端
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="options">选项</param>
|
||||
/// <param name="options">json序列化选项</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
public CardClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<CardClient> logger)
|
||||
{
|
||||
@@ -38,12 +39,13 @@ internal class CardClient
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>桌面小组件数据</returns>
|
||||
[ApiInformation<WidgetData>(Cookie = CookieType.Stoken)]
|
||||
[ApiInformation<WidgetData>(Cookie = CookieType.Stoken, Salt = SaltType.X6)]
|
||||
public async Task<WidgetData?> GetWidgetDataAsync(User user, CancellationToken token)
|
||||
{
|
||||
Response<DataWrapper<WidgetData>>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
.TryCatchGetFromJsonAsync<Response<DataWrapper<WidgetData>>>(ApiEndpoints.UserFullInfo, options, logger, token)
|
||||
.UsingDynamicSecret2(SaltType.X6, options, ApiEndpoints.AppWidgetData)
|
||||
.TryCatchGetFromJsonAsync<Response<DataWrapper<WidgetData>>>(logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data?.Data;
|
||||
|
||||
@@ -10,8 +10,10 @@ namespace Snap.Hutao.Web.Hoyolab;
|
||||
[SuppressMessage("", "SA1600")]
|
||||
public partial class Cookie
|
||||
{
|
||||
public const string COOKIE_TOKEN = "cookie_token";
|
||||
public const string ACCOUNT_ID = "account_id";
|
||||
public const string COOKIE_TOKEN = "cookie_token";
|
||||
|
||||
public const string E_HK4E_TOKEN = "e_hk4e_token";
|
||||
|
||||
public const string LOGIN_TICKET = "login_ticket";
|
||||
public const string LOGIN_UID = "login_uid";
|
||||
@@ -19,8 +21,8 @@ public partial class Cookie
|
||||
public const string LTOKEN = "ltoken";
|
||||
public const string LTUID = "ltuid";
|
||||
|
||||
public const string MID = "mid";
|
||||
|
||||
public const string STOKEN = "stoken";
|
||||
public const string STUID = "stuid";
|
||||
|
||||
public const string MID = "mid";
|
||||
}
|
||||
|
||||
@@ -16,15 +16,19 @@ internal abstract class DynamicSecretProvider : Md5Convert
|
||||
/// <summary>
|
||||
/// 创建动态密钥
|
||||
/// </summary>
|
||||
/// <param name="saltType">SALT 类型</param>
|
||||
/// <returns>密钥</returns>
|
||||
public static string Create()
|
||||
public static string Create(SaltType saltType)
|
||||
{
|
||||
Verify.Operation(saltType is SaltType.K2 or SaltType.LK2, "SALT 值无效");
|
||||
|
||||
// unix timestamp
|
||||
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
string r = GetRandomString();
|
||||
|
||||
string check = ToHexString($"salt={Core.CoreEnvironment.DynamicSecret1Salt}&t={t}&r={r}").ToLowerInvariant();
|
||||
string salt = saltType == SaltType.K2 ? Core.CoreEnvironment.DynamicSecretK2Salt : Core.CoreEnvironment.DynamicSecretLK2Salt;
|
||||
string check = ToHexString($"salt={salt}&t={t}&r={r}").ToLowerInvariant();
|
||||
|
||||
return $"{t},{r},{check}";
|
||||
}
|
||||
|
||||
@@ -13,12 +13,15 @@ internal abstract class DynamicSecretProvider2 : Md5Convert
|
||||
/// <summary>
|
||||
/// 创建动态密钥
|
||||
/// </summary>
|
||||
/// <param name="saltType">SALT 类型</param>
|
||||
/// <param name="options">json格式化器</param>
|
||||
/// <param name="queryUrl">查询url</param>
|
||||
/// <param name="postBody">请求体</param>
|
||||
/// <returns>密钥</returns>
|
||||
public static string Create(JsonSerializerOptions options, string queryUrl, object? postBody = null)
|
||||
public static string Create(SaltType saltType, JsonSerializerOptions options, string queryUrl, object? postBody = null)
|
||||
{
|
||||
Verify.Operation(saltType is SaltType.X6 or SaltType.X4, "SALT 值无效");
|
||||
|
||||
// unix timestamp
|
||||
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
@@ -33,7 +36,8 @@ internal abstract class DynamicSecretProvider2 : Md5Convert
|
||||
string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty;
|
||||
|
||||
// check
|
||||
string check = ToHexString($"salt={Core.CoreEnvironment.DynamicSecret2Salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant();
|
||||
string salt = saltType == SaltType.X6 ? Core.CoreEnvironment.DynamicSecretX6Salt : Core.CoreEnvironment.DynamicSecretX4Salt;
|
||||
string check = ToHexString($"salt={salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant();
|
||||
|
||||
return $"{t},{r},{check}";
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Web.Request;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
@@ -14,6 +15,7 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret.Http;
|
||||
internal class DynamicSecretHttpClient : IDynamicSecretHttpClient
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly SaltType saltType;
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly string url;
|
||||
|
||||
@@ -21,12 +23,13 @@ internal class DynamicSecretHttpClient : IDynamicSecretHttpClient
|
||||
/// 构造一个新的使用动态密钥2的Http客户端默认实现的实例
|
||||
/// </summary>
|
||||
/// <param name="httpClient">请求使用的客户端</param>
|
||||
/// <param name="saltType">SALT 类型</param>
|
||||
/// <param name="options">Json序列化选项</param>
|
||||
/// <param name="url">url</param>
|
||||
/// <param name="data">请求的数据</param>
|
||||
public DynamicSecretHttpClient(HttpClient httpClient, JsonSerializerOptions options, string url)
|
||||
public DynamicSecretHttpClient(HttpClient httpClient, SaltType saltType, JsonSerializerOptions options, string url)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.saltType = saltType;
|
||||
this.options = options;
|
||||
this.url = url;
|
||||
|
||||
@@ -38,6 +41,35 @@ internal class DynamicSecretHttpClient : IDynamicSecretHttpClient
|
||||
{
|
||||
return httpClient.GetFromJsonAsync<TValue>(url, options, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<T?> TryCatchGetFromJsonAsync<T>(CancellationToken token = default)
|
||||
where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
return await httpClient.GetFromJsonAsync<T>(url, options, token).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<T?> TryCatchGetFromJsonAsync<T>(ILogger logger, CancellationToken token = default)
|
||||
where T : class
|
||||
{
|
||||
try
|
||||
{
|
||||
return await httpClient.GetFromJsonAsync<T>(url, options, token).ConfigureAwait(false);
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogWarning(EventIds.HttpException, ex, "请求异常已忽略");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,6 +81,7 @@ internal class DynamicSecretHttpClient<TValue> : IDynamicSecretHttpClient<TValue
|
||||
where TValue : class
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly SaltType saltType;
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly string url;
|
||||
private readonly TValue data;
|
||||
@@ -57,12 +90,14 @@ internal class DynamicSecretHttpClient<TValue> : IDynamicSecretHttpClient<TValue
|
||||
/// 构造一个新的使用动态密钥2的Http客户端默认实现的实例
|
||||
/// </summary>
|
||||
/// <param name="httpClient">请求使用的客户端</param>
|
||||
/// <param name="saltType">SALT 类型</param>
|
||||
/// <param name="options">Json序列化选项</param>
|
||||
/// <param name="url">url</param>
|
||||
/// <param name="data">请求的数据</param>
|
||||
public DynamicSecretHttpClient(HttpClient httpClient, JsonSerializerOptions options, string url, TValue data)
|
||||
public DynamicSecretHttpClient(HttpClient httpClient, SaltType saltType, JsonSerializerOptions options, string url, TValue data)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.saltType = saltType;
|
||||
this.options = options;
|
||||
this.url = url;
|
||||
this.data = data;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret.Http;
|
||||
|
||||
@@ -17,6 +18,14 @@ internal interface IDynamicSecretHttpClient
|
||||
/// <param name="token">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
|
||||
/// <returns>The task object representing the asynchronous operation.</returns>
|
||||
Task<TValue?> GetFromJsonAsync<TValue>(CancellationToken token);
|
||||
|
||||
/// <inheritdoc cref="HttpClientJsonExtensions.GetFromJsonAsync{TValue}(HttpClient, string?, JsonSerializerOptions?, CancellationToken)"/>
|
||||
Task<T?> TryCatchGetFromJsonAsync<T>(CancellationToken token = default)
|
||||
where T : class;
|
||||
|
||||
/// <inheritdoc cref="HttpClientJsonExtensions.GetFromJsonAsync{TValue}(HttpClient, string?, JsonSerializerOptions?, CancellationToken)"/>
|
||||
Task<T?> TryCatchGetFromJsonAsync<T>(ILogger logger, CancellationToken token = default(CancellationToken))
|
||||
where T : class;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -13,40 +13,43 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
internal static class HttpClientDynamicSecretExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 使用一代动态密钥执行 GET 操作
|
||||
/// 使用一代动态密钥执行 GET/POST 操作
|
||||
/// </summary>
|
||||
/// <param name="httpClient">请求器</param>
|
||||
/// <param name="type">SALT 类型</param>
|
||||
/// <returns>响应</returns>
|
||||
public static HttpClient UsingDynamicSecret(this HttpClient httpClient)
|
||||
public static HttpClient UsingDynamicSecret1(this HttpClient httpClient, SaltType type)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Set("DS", DynamicSecretProvider.Create());
|
||||
httpClient.DefaultRequestHeaders.Set("DS", DynamicSecretProvider.Create(type));
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用二代动态密钥执行 GET 操作
|
||||
/// 使用二代动态密钥执行 GET/POST 操作
|
||||
/// </summary>
|
||||
/// <param name="httpClient">请求器</param>
|
||||
/// <param name="type">SALT 类型</param>
|
||||
/// <param name="options">选项</param>
|
||||
/// <param name="url">地址</param>
|
||||
/// <returns>响应</returns>
|
||||
public static IDynamicSecretHttpClient UsingDynamicSecret(this HttpClient httpClient, JsonSerializerOptions options, string url)
|
||||
public static IDynamicSecretHttpClient UsingDynamicSecret2(this HttpClient httpClient, SaltType type, JsonSerializerOptions options, string url)
|
||||
{
|
||||
return new DynamicSecretHttpClient(httpClient, options, url);
|
||||
return new DynamicSecretHttpClient(httpClient, type, options, url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用二代动态密钥执行 GET 操作
|
||||
/// 使用二代动态密钥执行 GET/POST 操作
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">请求数据的类型</typeparam>
|
||||
/// <param name="httpClient">请求器</param>
|
||||
/// <param name="type">SALT 类型</param>
|
||||
/// <param name="options">选项</param>
|
||||
/// <param name="url">地址</param>
|
||||
/// <param name="data">post数据</param>
|
||||
/// <returns>响应</returns>
|
||||
public static IDynamicSecretHttpClient<TValue> UsingDynamicSecret<TValue>(this HttpClient httpClient, JsonSerializerOptions options, string url, TValue data)
|
||||
public static IDynamicSecretHttpClient<TValue> UsingDynamicSecret2<TValue>(this HttpClient httpClient, SaltType type, JsonSerializerOptions options, string url, TValue data)
|
||||
where TValue : class
|
||||
{
|
||||
return new DynamicSecretHttpClient<TValue>(httpClient, options, url, data);
|
||||
return new DynamicSecretHttpClient<TValue>(httpClient, type, options, url, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
|
||||
/// <summary>
|
||||
/// Salt's type
|
||||
/// </summary>
|
||||
public enum SaltType
|
||||
{
|
||||
/// <summary>
|
||||
/// 不需要 SALT
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// X4
|
||||
/// </summary>
|
||||
X4,
|
||||
|
||||
/// <summary>
|
||||
/// X6
|
||||
/// </summary>
|
||||
X6,
|
||||
|
||||
/// <summary>
|
||||
/// K2
|
||||
/// </summary>
|
||||
K2,
|
||||
|
||||
/// <summary>
|
||||
/// LK2
|
||||
/// </summary>
|
||||
LK2,
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using Snap.Hutao.Core.Logging;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
using Snap.Hutao.Web.Request;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
@@ -66,7 +67,7 @@ internal static class HttpClientExtensions
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="user">绑定用户</param>
|
||||
/// <returns>客户端</returns>
|
||||
[Obsolete("请使用带有 type 的重载")]
|
||||
[Obsolete("请使用带有 cookie 的重载")]
|
||||
internal static HttpClient SetUser(this HttpClient httpClient, User user)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString());
|
||||
@@ -78,11 +79,11 @@ internal static class HttpClientExtensions
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="user">实体用户</param>
|
||||
/// <param name="type">Cookie类型</param>
|
||||
/// <param name="cookie">Cookie类型</param>
|
||||
/// <returns>客户端</returns>
|
||||
internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user, CookieType type = CookieType.All)
|
||||
internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user, CookieType cookie = CookieType.All)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString(type));
|
||||
httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString(cookie));
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
32
src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/Link.cs
Normal file
32
src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/Link.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Passport;
|
||||
|
||||
/// <summary>
|
||||
/// 链接
|
||||
/// </summary>
|
||||
public class Link
|
||||
{
|
||||
/// <summary>
|
||||
/// 第三方 sn
|
||||
/// </summary>
|
||||
[JsonPropertyName("thirdparty")]
|
||||
public string Thirdparty { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Union Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("union_id")]
|
||||
public string UnionId { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 昵称
|
||||
/// </summary>
|
||||
[JsonPropertyName("nickname")]
|
||||
public string Nickname { get; set; } = default!;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// 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.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Passport;
|
||||
|
||||
/// <summary>
|
||||
/// 通行证客户端
|
||||
/// </summary>
|
||||
[HttpClient(HttpClientConfigration.Default)]
|
||||
internal class PassportClient
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly JsonSerializerOptions options;
|
||||
private readonly ILogger<PassportClient> logger;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的通行证客户端
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="options">json序列化选项</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
public PassportClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<PassportClient> logger)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.options = options;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步验证Ltoken
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>验证信息</returns>
|
||||
[ApiInformation<VerifyInformation>(Cookie = CookieType.All)]
|
||||
public async Task<VerifyInformation?> VerifyLtokenAsync(User user, CancellationToken token)
|
||||
{
|
||||
Response<VerifyInformation>? response = await httpClient
|
||||
.SetUser(user, CookieType.All)
|
||||
.TryCatchPostAsJsonAsync<Timestamp, Response<VerifyInformation>>(ApiEndpoints.AccountVerifyLtoken, new(), options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return response?.Data;
|
||||
}
|
||||
|
||||
private class Timestamp
|
||||
{
|
||||
public Timestamp()
|
||||
{
|
||||
T = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
}
|
||||
|
||||
[JsonPropertyName("t")]
|
||||
public long T { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
|
||||
using Snap.Hutao.Web.Response;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Passport;
|
||||
|
||||
/// <summary>
|
||||
/// 验证返回信息
|
||||
/// </summary>
|
||||
public class VerifyInformation
|
||||
{
|
||||
/// <summary>
|
||||
/// 账户Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("aid")]
|
||||
public string Aid { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 米哈游Id
|
||||
/// </summary>
|
||||
[JsonPropertyName("mid")]
|
||||
public string Mid { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 空
|
||||
/// </summary>
|
||||
[JsonPropertyName("account_name")]
|
||||
public string AccountName { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 空
|
||||
/// </summary>
|
||||
[JsonPropertyName("email")]
|
||||
public string Email { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为邮箱验证
|
||||
/// </summary>
|
||||
[JsonPropertyName("is_email_verify")]
|
||||
public int IsEmailVerify { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 区域码 +86
|
||||
/// </summary>
|
||||
[JsonPropertyName("area_code")]
|
||||
public string AreaCode { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 手机号 111****1111
|
||||
/// </summary>
|
||||
[JsonPropertyName("mobile")]
|
||||
public string Mobile { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 安全手机区域码 +86
|
||||
/// </summary>
|
||||
[JsonPropertyName("safe_area_code")]
|
||||
public string SafeAreaCode { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 安全手机号 111****1111
|
||||
/// </summary>
|
||||
[JsonPropertyName("safe_mobile")]
|
||||
public string SafeMobile { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 真名 **某
|
||||
/// </summary>
|
||||
[JsonPropertyName("realname")]
|
||||
public string Realname { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 身份证号 111************111
|
||||
/// </summary>
|
||||
[JsonPropertyName("identity_code")]
|
||||
public string IdentityCode { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 重新绑定区域码
|
||||
/// </summary>
|
||||
[JsonPropertyName("rebind_area_code")]
|
||||
public string RebindAreaCode { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 重新绑定的手机号
|
||||
/// </summary>
|
||||
[JsonPropertyName("rebind_mobile")]
|
||||
public string RebindMobile { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 重新绑定时间 "0"
|
||||
/// </summary>
|
||||
[JsonPropertyName("rebind_mobile_time")]
|
||||
public string RebindMobileTime { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 链接
|
||||
/// </summary>
|
||||
[JsonPropertyName("links")]
|
||||
public List<Link> Links { get; set; } = default!;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ internal class BindingClient2
|
||||
Response<GameAuthKey>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
.SetReferer("https://app.mihoyo.com")
|
||||
.UsingDynamicSecret()
|
||||
.UsingDynamicSecret1()
|
||||
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.GenAuthKey, data, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ internal class GameRecordClient
|
||||
{
|
||||
Response<DailyNote.DailyNote>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
.UsingDynamicSecret(options, ApiEndpoints.GameRecordDailyNote(uid.Value, uid.Region))
|
||||
.UsingDynamicSecret2(options, ApiEndpoints.GameRecordDailyNote(uid.Value, uid.Region))
|
||||
.GetFromJsonAsync<Response<DailyNote.DailyNote>>(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -63,7 +63,7 @@ internal class GameRecordClient
|
||||
{
|
||||
Response<DailyNote.DailyNote>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
.UsingDynamicSecret(options, ApiEndpoints.GameRecordDailyNote(uid.Value, uid.Region))
|
||||
.UsingDynamicSecret2(options, ApiEndpoints.GameRecordDailyNote(uid.Value, uid.Region))
|
||||
.GetFromJsonAsync<Response<DailyNote.DailyNote>>(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -93,7 +93,7 @@ internal class GameRecordClient
|
||||
{
|
||||
Response<PlayerInfo>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
.UsingDynamicSecret(options, ApiEndpoints.GameRecordIndex(uid.Value, uid.Region))
|
||||
.UsingDynamicSecret2(options, ApiEndpoints.GameRecordIndex(uid.Value, uid.Region))
|
||||
.GetFromJsonAsync<Response<PlayerInfo>>(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -125,7 +125,7 @@ internal class GameRecordClient
|
||||
{
|
||||
Response<SpiralAbyss.SpiralAbyss>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
.UsingDynamicSecret(options, ApiEndpoints.GameRecordSpiralAbyss(schedule, uid))
|
||||
.UsingDynamicSecret2(options, ApiEndpoints.GameRecordSpiralAbyss(schedule, uid))
|
||||
.GetFromJsonAsync<Response<SpiralAbyss.SpiralAbyss>>(token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
@@ -159,7 +159,7 @@ internal class GameRecordClient
|
||||
|
||||
Response<CharacterWrapper>? resp = await httpClient
|
||||
.SetUser(user)
|
||||
.UsingDynamicSecret(options, ApiEndpoints.GameRecordCharacter, data)
|
||||
.UsingDynamicSecret2(options, ApiEndpoints.GameRecordCharacter, data)
|
||||
.TryCatchPostAsJsonAsync<Response<CharacterWrapper>>(logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user