support verifyLtoken for #207

This commit is contained in:
DismissedLight
2022-11-13 18:04:08 +08:00
parent a150c4a04c
commit 58b34ea60a
17 changed files with 355 additions and 36 deletions

View File

@@ -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

View File

@@ -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; }
}

View File

@@ -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";

View File

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

View File

@@ -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";
}

View File

@@ -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}";
}

View File

@@ -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}";
}

View File

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

View File

@@ -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>

View File

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

View File

@@ -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,
}

View File

@@ -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;
}

View 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!;
}

View File

@@ -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; }
}
}

View File

@@ -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!;
}

View File

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

View File

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