refactor done

This commit is contained in:
DismissedLight
2023-08-17 00:05:04 +08:00
parent c4e4ffebd6
commit 0bd8c01fdd
36 changed files with 137 additions and 147 deletions

View File

@@ -49,15 +49,14 @@ internal static partial class EnumerableExtension
return list.GetRange(start, length);
}
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)] this List<TSource>? source)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this List<TSource>? source)
{
if (source is { } list)
if (source is { Count: > 0 })
{
// empty
return list.Count <= 0;
return false;
}
// null
return true;
}

View File

@@ -54,6 +54,7 @@ internal sealed partial class LaunchGameViewModel : Abstraction.ViewModel
/// <summary>
/// 已知的服务器方案
/// </summary>
[SuppressMessage("", "CA1822")]
public List<LaunchScheme> KnownSchemes { get => LaunchScheme.GetKnownSchemes(); }
/// <summary>

View File

@@ -417,7 +417,8 @@ internal class MiHoYoJSInterface
private void OnNavigationStarting(CoreWebView2 coreWebView2, CoreWebView2NavigationStartingEventArgs args)
{
string uriHost = new Uri(args.Uri).Host;
if (uriHost.EndsWith("mihoyo.com") || uriHost.EndsWith("hoyolab.com"))
ReadOnlySpan<char> uriHostSpan = uriHost.AsSpan();
if (uriHostSpan.EndsWith("mihoyo.com") || uriHostSpan.EndsWith("hoyolab.com"))
{
// Execute this solve issue: When open same site second time,there might be no bridge init.
coreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(InitializeJsInterfaceScript2).AsTask().SafeForget(logger);

View File

@@ -34,7 +34,7 @@ internal sealed partial class EnkaClient
/// <returns>Enka API 响应</returns>
public ValueTask<EnkaResponse?> GetForwardDataAsync(in PlayerUid playerUid, CancellationToken token = default)
{
return TryGetEnkaResponseCoreAsync(string.Format(EnkaAPIHutaoForward, playerUid.Value), token);
return TryGetEnkaResponseCoreAsync(EnkaAPIHutaoForward.Format(playerUid.Value), token);
}
/// <summary>

View File

@@ -45,7 +45,7 @@ internal sealed partial class Cookie
public static Cookie Parse(string cookieString)
{
SortedDictionary<string, string> cookieMap = new();
cookieString = cookieString.Replace(" ", string.Empty);
cookieString = cookieString.Replace(" ", string.Empty, StringComparison.Ordinal);
string[] values = cookieString.Split(';', StringSplitOptions.RemoveEmptyEntries);
foreach (string[] parts in values.Select(c => c.Split('=', 2)))
{
@@ -76,7 +76,7 @@ internal sealed partial class Cookie
/// <returns>Cookie</returns>
public static Cookie FromLoginResult(LoginResult? loginResult)
{
if (loginResult == null)
if (loginResult is null)
{
return new();
}
@@ -115,15 +115,12 @@ internal sealed partial class Cookie
public bool TryGetLToken([NotNullWhen(true)] out Cookie? cookie)
{
bool hasLtoken = TryGetValue(LTOKEN, out string? ltoken);
bool hasStuid = TryGetValue(LTUID, out string? ltuid);
if (hasLtoken && hasStuid)
if (TryGetValue(LTOKEN, out string? ltoken) && TryGetValue(LTUID, out string? ltuid))
{
cookie = new Cookie(new()
{
[LTOKEN] = ltoken!,
[LTUID] = ltuid!,
[LTOKEN] = ltoken,
[LTUID] = ltuid,
});
return true;
@@ -135,15 +132,12 @@ internal sealed partial class Cookie
public bool TryGetCookieToken([NotNullWhen(true)] out Cookie? cookie)
{
bool hasAccountId = TryGetValue(ACCOUNT_ID, out string? accountId);
bool hasCookieToken = TryGetValue(COOKIE_TOKEN, out string? cookieToken);
if (hasAccountId && hasCookieToken)
if (TryGetValue(ACCOUNT_ID, out string? accountId) && TryGetValue(COOKIE_TOKEN, out string? cookieToken))
{
cookie = new Cookie(new()
{
[ACCOUNT_ID] = accountId!,
[COOKIE_TOKEN] = cookieToken!,
[ACCOUNT_ID] = accountId,
[COOKIE_TOKEN] = cookieToken,
});
return true;
@@ -180,17 +174,13 @@ internal sealed partial class Cookie
private bool TryGetSToken([NotNullWhen(true)] out Cookie? cookie)
{
bool hasMid = TryGetValue(MID, out string? mid);
bool hasSToken = TryGetValue(STOKEN, out string? stoken);
bool hasSTuid = TryGetValue(STUID, out string? stuid);
if (hasMid && hasSToken && hasSTuid)
if (TryGetValue(MID, out string? mid) && TryGetValue(STOKEN, out string? stoken) && TryGetValue(STUID, out string? stuid))
{
cookie = new Cookie(new()
{
[MID] = mid!,
[STOKEN] = stoken!,
[STUID] = stuid!,
[MID] = mid,
[STOKEN] = stoken,
[STUID] = stuid,
});
return true;
@@ -202,15 +192,12 @@ internal sealed partial class Cookie
private bool TryGetLegacySToken([NotNullWhen(true)] out Cookie? cookie)
{
bool hasSToken = TryGetValue(STOKEN, out string? stoken);
bool hasSTuid = TryGetValue(STUID, out string? stuid);
if (hasSToken && hasSTuid)
if (TryGetValue(STOKEN, out string? stoken) && TryGetValue(STUID, out string? stuid))
{
cookie = new Cookie(new()
{
[STOKEN] = stoken!,
[STUID] = stuid!,
[STOKEN] = stoken,
[STUID] = stuid,
});
return true;

View File

@@ -31,7 +31,8 @@ internal sealed class DynamicSecretHandler : DelegatingHandler
return await base.SendAsync(request, token).ConfigureAwait(false);
}
private static async Task ProcessRequestWithOptionsAsync(HttpRequestMessage request, DynamicSecretCreationOptions options, CancellationToken token)
[SuppressMessage("", "CA1308")]
private static async ValueTask ProcessRequestWithOptionsAsync(HttpRequestMessage request, DynamicSecretCreationOptions options, CancellationToken token)
{
string salt = HoyolabOptions.Salts[options.SaltType];
@@ -44,11 +45,12 @@ internal sealed class DynamicSecretHandler : DelegatingHandler
// ds2 b & q process
if (options.Version == DynamicSecretVersion.Gen2)
{
string b = request.Content != null
string b = request.Content is not null
? await request.Content.ReadAsStringAsync(token).ConfigureAwait(false)
: options.DefaultBody; // PROD's default value is {}
string[] queries = Uri.UnescapeDataString(request.RequestUri!.Query).Split('?', 2);
ArgumentNullException.ThrowIfNull(request.RequestUri);
string[] queries = Uri.UnescapeDataString(request.RequestUri.Query).Split('?', 2);
string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty;
dsContent = $"{dsContent}&b={b}&q={q}";

View File

@@ -9,6 +9,6 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
/// </summary>
[HighQuality]
[AttributeUsage(AttributeTargets.Class)]
internal class UseDynamicSecretAttribute : Attribute
internal sealed class UseDynamicSecretAttribute : Attribute
{
}

View File

@@ -32,20 +32,20 @@ internal sealed class Announcement : AnnouncementContent
TimeSpan span = StartTime - now;
if (span.TotalDays <= 1)
{
return string.Format(SH.WebAnnouncementTimeHoursBeginFormat, (int)span.TotalHours);
return SH.WebAnnouncementTimeHoursBeginFormat.Format((int)span.TotalHours);
}
return string.Format(SH.WebAnnouncementTimeDaysBeginFormat, (int)span.TotalDays);
return SH.WebAnnouncementTimeDaysBeginFormat.Format((int)span.TotalDays);
}
else
{
TimeSpan span = EndTime - now;
if (span.TotalDays <= 1)
{
return string.Format(SH.WebAnnouncementTimeHoursEndFormat, (int)span.TotalHours);
return SH.WebAnnouncementTimeHoursEndFormat.Format((int)span.TotalHours);
}
return string.Format(SH.WebAnnouncementTimeDaysEndFormat, (int)span.TotalDays);
return SH.WebAnnouncementTimeDaysEndFormat.Format((int)span.TotalDays);
}
}
}

View File

@@ -23,7 +23,7 @@ internal sealed partial class AnnouncementClient
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>公告列表</returns>
public async Task<Response<AnnouncementWrapper>> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
public async ValueTask<Response<AnnouncementWrapper>> GetAnnouncementsAsync(CancellationToken cancellationToken = default)
{
Response<AnnouncementWrapper>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<AnnouncementWrapper>>(ApiEndpoints.AnnList, jsonSerializerOptions, logger, cancellationToken)
@@ -37,7 +37,7 @@ internal sealed partial class AnnouncementClient
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>公告内容列表</returns>
public async Task<Response<ListWrapper<AnnouncementContent>>> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
public async ValueTask<Response<ListWrapper<AnnouncementContent>>> GetAnnouncementContentsAsync(CancellationToken cancellationToken = default)
{
Response<ListWrapper<AnnouncementContent>>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<ListWrapper<AnnouncementContent>>>(ApiEndpoints.AnnContent, jsonSerializerOptions, logger, cancellationToken)

View File

@@ -25,7 +25,7 @@ internal sealed partial class GachaInfoClient
/// <param name="options">查询</param>
/// <param name="token">取消令牌</param>
/// <returns>单个祈愿记录页面</returns>
public async Task<Response<GachaLogPage>> GetGachaLogPageAsync(GachaLogQueryOptions options, CancellationToken token = default)
public async ValueTask<Response<GachaLogPage>> GetGachaLogPageAsync(GachaLogQueryOptions options, CancellationToken token = default)
{
string query = options.ToQueryString();

View File

@@ -86,7 +86,7 @@ internal struct GachaLogQueryOptions
/// 转换到查询字符串
/// </summary>
/// <returns>匹配的查询字符串</returns>
public string ToQueryString()
public readonly string ToQueryString()
{
// Make the cached end id into query.
innerQuery.Set("end_id", EndId);

View File

@@ -27,17 +27,17 @@ internal static class HoyolabHttpClientExtension
if (cookie.HasFlag(CookieType.CookieToken))
{
stringBuilder.Append(user.CookieToken).AppendIf(user.CookieToken != null, ';');
stringBuilder.Append(user.CookieToken).AppendIf(user.CookieToken is not null, ';');
}
if (cookie.HasFlag(CookieType.LToken))
{
stringBuilder.Append(user.LToken).AppendIf(user.LToken != null, ';');
stringBuilder.Append(user.LToken).AppendIf(user.LToken is not null, ';');
}
if (cookie.HasFlag(CookieType.SToken))
{
stringBuilder.Append(user.SToken).AppendIf(user.SToken != null, ';');
stringBuilder.Append(user.SToken).AppendIf(user.SToken is not null, ';');
}
string result = stringBuilder.ToString();

View File

@@ -17,7 +17,7 @@ internal interface IPassportClient
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>cookie token</returns>
Task<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default);
ValueTask<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default);
/// <summary>
/// 异步获取 LToken
@@ -25,5 +25,5 @@ internal interface IPassportClient
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>uid 与 cookie token</returns>
Task<Response<LTokenWrapper>> GetLTokenBySTokenAsync(User user, CancellationToken token = default);
ValueTask<Response<LTokenWrapper>> GetLTokenBySTokenAsync(User user, CancellationToken token = default);
}

View File

@@ -30,7 +30,7 @@ internal sealed partial class PassportClient : IPassportClient
/// <param name="token">取消令牌</param>
/// <returns>cookie token</returns>
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.PROD)]
public async Task<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default)
public async ValueTask<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default)
{
Response<UidCookieToken>? resp = await httpClient
.SetUser(user, CookieType.SToken)
@@ -48,7 +48,7 @@ internal sealed partial class PassportClient : IPassportClient
/// <param name="token">取消令牌</param>
/// <returns>uid 与 cookie token</returns>
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.PROD)]
public async Task<Response<LTokenWrapper>> GetLTokenBySTokenAsync(User user, CancellationToken token = default)
public async ValueTask<Response<LTokenWrapper>> GetLTokenBySTokenAsync(User user, CancellationToken token = default)
{
Response<LTokenWrapper>? resp = await httpClient
.SetUser(user, CookieType.SToken)

View File

@@ -31,7 +31,7 @@ internal sealed partial class PassportClient2
/// <param name="token">取消令牌</param>
/// <returns>验证信息</returns>
[ApiInformation(Cookie = CookieType.LToken)]
public async Task<Response<UserInfoWrapper>> VerifyLtokenAsync(User user, CancellationToken token)
public async ValueTask<Response<UserInfoWrapper>> VerifyLtokenAsync(User user, CancellationToken token)
{
Response<UserInfoWrapper>? response = await httpClient
.SetUser(user, CookieType.LToken)
@@ -48,7 +48,7 @@ internal sealed partial class PassportClient2
/// <param name="token">取消令牌</param>
/// <returns>登录数据</returns>
[ApiInformation(Salt = SaltType.PROD)]
public async Task<Response<LoginResult>> LoginBySTokenAsync(Cookie stokenV1, CancellationToken token = default)
public async ValueTask<Response<LoginResult>> LoginBySTokenAsync(Cookie stokenV1, CancellationToken token = default)
{
HttpResponseMessage message = await httpClient
.SetHeader("Cookie", stokenV1.ToString())

View File

@@ -27,9 +27,12 @@ internal sealed partial class PassportClientOversea : IPassportClient
/// <param name="token">取消令牌</param>
/// <returns>cookie token</returns>
[ApiInformation(Cookie = CookieType.SToken)]
public async Task<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default)
public async ValueTask<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default)
{
STokenWrapper data = new(user.SToken?.GetValueOrDefault(Cookie.STOKEN)!, user.Aid!);
string? stoken = user.SToken?.GetValueOrDefault(Cookie.STOKEN);
ArgumentException.ThrowIfNullOrEmpty(stoken);
ArgumentException.ThrowIfNullOrEmpty(user.Aid);
STokenWrapper data = new(stoken, user.Aid);
Response<UidCookieToken>? resp = await httpClient
.SetUser(user, CookieType.SToken)
@@ -46,9 +49,12 @@ internal sealed partial class PassportClientOversea : IPassportClient
/// <param name="token">取消令牌</param>
/// <returns>uid 与 cookie token</returns>
[ApiInformation(Cookie = CookieType.SToken)]
public async Task<Response<LTokenWrapper>> GetLTokenBySTokenAsync(User user, CancellationToken token = default)
public async ValueTask<Response<LTokenWrapper>> GetLTokenBySTokenAsync(User user, CancellationToken token = default)
{
STokenWrapper data = new(user.SToken?.GetValueOrDefault(Cookie.STOKEN)!, user.Aid!);
string? stoken = user.SToken?.GetValueOrDefault(Cookie.STOKEN);
ArgumentException.ThrowIfNullOrEmpty(stoken);
ArgumentException.ThrowIfNullOrEmpty(user.Aid);
STokenWrapper data = new(stoken, user.Aid);
Response<LTokenWrapper>? resp = await httpClient
.SetUser(user, CookieType.SToken)

View File

@@ -26,7 +26,7 @@ internal sealed partial class ResourceClient
/// <param name="scheme">方案</param>
/// <param name="token">取消令牌</param>
/// <returns>游戏资源</returns>
public async Task<Response<GameResource>> GetResourceAsync(LaunchScheme scheme, CancellationToken token = default)
public async ValueTask<Response<GameResource>> GetResourceAsync(LaunchScheme scheme, CancellationToken token = default)
{
string url = scheme.IsOversea
? ApiOsEndpoints.SdkOsStaticLauncherResource(scheme)

View File

@@ -31,9 +31,10 @@ internal sealed partial class AuthClient
/// <param name="user">用户</param>
/// <returns>操作凭证</returns>
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.K2)]
public async Task<Response<ActionTicketWrapper>> GetActionTicketBySTokenAsync(string action, User user)
public async ValueTask<Response<ActionTicketWrapper>> GetActionTicketBySTokenAsync(string action, User user)
{
string url = ApiEndpoints.AuthActionTicket(action, user.SToken?[Cookie.STOKEN] ?? string.Empty, user.Aid!);
ArgumentException.ThrowIfNullOrEmpty(user.Aid);
string url = ApiEndpoints.AuthActionTicket(action, user.SToken?[Cookie.STOKEN] ?? string.Empty, user.Aid);
Response<ActionTicketWrapper>? resp = await httpClient
.SetUser(user, CookieType.SToken)
@@ -51,7 +52,7 @@ internal sealed partial class AuthClient
/// <param name="isOversea">是否为国际服</param>
/// <param name="token">取消令牌</param>
/// <returns>包含token的字典</returns>
public async Task<Response<ListWrapper<NameToken>>> GetMultiTokenByLoginTicketAsync(Cookie cookie, bool isOversea, CancellationToken token = default)
public async ValueTask<Response<ListWrapper<NameToken>>> GetMultiTokenByLoginTicketAsync(Cookie cookie, bool isOversea, CancellationToken token = default)
{
string loginTicket = cookie[Cookie.LOGIN_TICKET];
string loginUid = cookie[Cookie.LOGIN_UID];

View File

@@ -30,7 +30,7 @@ internal sealed partial class BindingClient
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
public async Task<Response<ListWrapper<UserGameRole>>> GetUserGameRolesOverseaAwareAsync(User user, CancellationToken token = default)
public async ValueTask<Response<ListWrapper<UserGameRole>>> GetUserGameRolesOverseaAwareAsync(User user, CancellationToken token = default)
{
if (user.IsOversea)
{
@@ -63,7 +63,7 @@ internal sealed partial class BindingClient
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.LToken)]
public async Task<Response<ListWrapper<UserGameRole>>> GetUserGameRolesByActionTicketAsync(string actionTicket, User user, CancellationToken token = default)
public async ValueTask<Response<ListWrapper<UserGameRole>>> GetUserGameRolesByActionTicketAsync(string actionTicket, User user, CancellationToken token = default)
{
string url = ApiEndpoints.UserGameRolesByActionTicket(actionTicket);
@@ -82,7 +82,7 @@ internal sealed partial class BindingClient
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.LToken)]
public async Task<Response<ListWrapper<UserGameRole>>> GetOverseaUserGameRolesByCookieAsync(User user, CancellationToken token = default)
public async ValueTask<Response<ListWrapper<UserGameRole>>> GetOverseaUserGameRolesByCookieAsync(User user, CancellationToken token = default)
{
Response<ListWrapper<UserGameRole>>? resp = await httpClient
.SetUser(user, CookieType.LToken)

View File

@@ -30,7 +30,7 @@ internal sealed partial class BindingClient2
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.LK2)]
public async Task<List<UserGameRole>> GetUserGameRolesBySTokenAsync(User user, CancellationToken token = default)
public async ValueTask<List<UserGameRole>> GetUserGameRolesBySTokenAsync(User user, CancellationToken token = default)
{
Response<ListWrapper<UserGameRole>>? resp = await httpClient
.SetUser(user, CookieType.SToken)
@@ -50,7 +50,7 @@ internal sealed partial class BindingClient2
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.LK2)]
public async Task<Response<GameAuthKey>> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
public async ValueTask<Response<GameAuthKey>> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
{
Response<GameAuthKey>? resp = await httpClient
.SetUser(user, CookieType.SToken)

View File

@@ -1,6 +1,8 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Globalization;
namespace Snap.Hutao.Web.Hoyolab.Takumi.Binding;
/// <summary>
@@ -22,7 +24,7 @@ internal sealed class GenAuthKeyData
{
AuthAppId = authAppId;
GameBiz = gameBiz;
GameUid = int.Parse(uid.Value);
GameUid = int.Parse(uid.Value, CultureInfo.InvariantCulture);
Region = uid.Region;
}

View File

@@ -29,7 +29,7 @@ internal sealed partial class CalculateClient
/// <param name="token">取消令牌</param>
/// <returns>消耗结果</returns>
[ApiInformation(Cookie = CookieType.Cookie)]
public async Task<Response<Consumption>> ComputeAsync(Model.Entity.User user, AvatarPromotionDelta delta, CancellationToken token = default)
public async ValueTask<Response<Consumption>> ComputeAsync(Model.Entity.User user, AvatarPromotionDelta delta, CancellationToken token = default)
{
string referer = user.IsOversea
? ApiOsEndpoints.ActHoyolabReferer
@@ -54,7 +54,7 @@ internal sealed partial class CalculateClient
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
public async Task<List<Avatar>> GetAvatarsAsync(UserAndUid userAndUid, CancellationToken token = default)
public async ValueTask<List<Avatar>> GetAvatarsAsync(UserAndUid userAndUid, CancellationToken token = default)
{
int currentPage = 1;
SyncAvatarFilter filter = new() { Uid = userAndUid.Uid.Value, Region = userAndUid.Uid.Region };
@@ -81,7 +81,7 @@ internal sealed partial class CalculateClient
.TryCatchPostAsJsonAsync<SyncAvatarFilter, Response<ListWrapper<Avatar>>>(url, filter, options, logger, token)
.ConfigureAwait(false);
if (resp != null && resp.IsOk())
if (resp is not null && resp.IsOk())
{
avatars.AddRange(resp.Data.List);
}
@@ -93,7 +93,7 @@ internal sealed partial class CalculateClient
await Task.Delay(Random.Shared.Next(0, 1000), token).ConfigureAwait(false);
}
while (resp?.Data?.List?.Count == 20);
while (resp.Data is { List.Count: 20 });
return avatars;
}
@@ -105,7 +105,7 @@ internal sealed partial class CalculateClient
/// <param name="avatar">角色</param>
/// <param name="token">取消令牌</param>
/// <returns>角色详情</returns>
public async Task<Response<AvatarDetail>> GetAvatarDetailAsync(UserAndUid userAndUid, Avatar avatar, CancellationToken token = default)
public async ValueTask<Response<AvatarDetail>> GetAvatarDetailAsync(UserAndUid userAndUid, Avatar avatar, CancellationToken token = default)
{
string url = userAndUid.User.IsOversea
? ApiOsEndpoints.CalculateSyncAvatarDetail(avatar.Id, userAndUid.Uid.Value)
@@ -126,7 +126,7 @@ internal sealed partial class CalculateClient
/// <param name="shareCode">摹本码</param>
/// <param name="token">取消令牌</param>
/// <returns>家具列表</returns>
public async Task<Response<FurnitureListWrapper>> FurnitureBlueprintAsync(Model.Entity.User user, string shareCode, CancellationToken token)
public async ValueTask<Response<FurnitureListWrapper>> FurnitureBlueprintAsync(Model.Entity.User user, string shareCode, CancellationToken token)
{
Response<FurnitureListWrapper>? resp = await httpClient
.SetUser(user, CookieType.CookieToken)
@@ -143,7 +143,7 @@ internal sealed partial class CalculateClient
/// <param name="items">物品</param>
/// <param name="token">取消令牌</param>
/// <returns>消耗</returns>
public async Task<Response<ListWrapper<Item>>> FurnitureComputeAsync(Model.Entity.User user, List<Item> items, CancellationToken token)
public async ValueTask<Response<ListWrapper<Item>>> FurnitureComputeAsync(Model.Entity.User user, List<Item> items, CancellationToken token)
{
ListWrapper<IdCount> data = new() { List = items.Select(i => new IdCount { Id = i.Id, Count = i.Num }).ToList() };

View File

@@ -22,16 +22,19 @@ internal static class ItemHelper
return new(0);
}
if (right.IsNullOrEmpty())
if (left.IsNullOrEmpty() && !right.IsNullOrEmpty())
{
return left!;
return right;
}
if (left.IsNullOrEmpty())
if (right.IsNullOrEmpty() && !left.IsNullOrEmpty())
{
return right!;
return left;
}
ArgumentNullException.ThrowIfNull(left);
ArgumentNullException.ThrowIfNull(right);
List<Item> result = new(left.Count + right.Count);
result.AddRange(left);

View File

@@ -15,33 +15,21 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
/// </summary>
[HighQuality]
[UseDynamicSecret]
[ConstructorGenerated(ResolveHttpClient = true)]
[HttpClient(HttpClientConfiguration.XRpc)]
internal sealed class CardClient
internal sealed partial class CardClient
{
private readonly HttpClient httpClient;
private readonly JsonSerializerOptions options;
private readonly ILogger<CardClient> logger;
/// <summary>
/// 构造一个新的卡片客户端
/// </summary>
/// <param name="httpClient">http客户端</param>
/// <param name="options">选项</param>
/// <param name="logger">日志器</param>
public CardClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<CardClient> logger)
{
this.httpClient = httpClient;
this.options = options;
this.logger = logger;
}
/// <summary>
/// 注册验证码
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>注册结果</returns>
public async Task<Response<VerificationRegistration>> CreateVerificationAsync(User user, CancellationToken token)
public async ValueTask<Response<VerificationRegistration>> CreateVerificationAsync(User user, CancellationToken token)
{
Response<VerificationRegistration>? resp = await httpClient
.SetUser(user, CookieType.LToken)
@@ -59,7 +47,7 @@ internal sealed class CardClient
/// <param name="validate">验证</param>
/// <param name="token">取消令牌</param>
/// <returns>验证结果</returns>
public async Task<Response<VerificationResult>> VerifyVerificationAsync(string challenge, string validate, CancellationToken token)
public async ValueTask<Response<VerificationResult>> VerifyVerificationAsync(string challenge, string validate, CancellationToken token)
{
VerificationData data = new(challenge, validate);
@@ -77,7 +65,7 @@ internal sealed class CardClient
/// <param name="token">取消令牌</param>
/// <returns>桌面小组件数据</returns>
[ApiInformation(Cookie = CookieType.SToken, Salt = SaltType.X6)]
public async Task<Response<DailyNote.WidgetDailyNote>> GetWidgetDataAsync(User user, CancellationToken token)
public async ValueTask<Response<DailyNote.WidgetDailyNote>> GetWidgetDataAsync(User user, CancellationToken token)
{
Response<DailyNote.WidgetDailyNote>? resp = await httpClient
.SetUser(user, CookieType.SToken)

View File

@@ -33,7 +33,7 @@ internal sealed class CardVerifier
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>流水号</returns>
public async Task<string?> TryGetXrpcChallengeAsync(User user, CancellationToken token)
public async ValueTask<string?> TryGetXrpcChallengeAsync(User user, CancellationToken token)
{
Response.Response<VerificationRegistration> registrationResponse = await cardClient.CreateVerificationAsync(user, token).ConfigureAwait(false);
if (registrationResponse.IsOk())
@@ -43,14 +43,14 @@ internal sealed class CardVerifier
await geetestClient.GetTypeAsync(registration.Gt).ConfigureAwait(false);
GeetestResult<GeetestData>? ajax = await geetestClient.GetAjaxAsync(registration).ConfigureAwait(false);
if (ajax?.Data.Validate is string validate)
if (ajax?.Data.Validate is { } validate)
{
Response.Response<VerificationResult> verifyResponse = await cardClient.VerifyVerificationAsync(registration.Challenge, validate, token).ConfigureAwait(false);
if (verifyResponse.IsOk())
{
VerificationResult result = verifyResponse.Data;
if (result.Challenge != null)
if (result.Challenge is not null)
{
return result.Challenge;
}
@@ -58,6 +58,6 @@ internal sealed class CardVerifier
}
}
return null;
return default;
}
}

View File

@@ -38,10 +38,10 @@ internal sealed class DailyNote : DailyNoteCommon
0 => SH.WebDailyNoteRecoveryTimeDay0,
1 => SH.WebDailyNoteRecoveryTimeDay1,
2 => SH.WebDailyNoteRecoveryTimeDay2,
_ => string.Format(SH.WebDailyNoteRecoveryTimeDayFormat, totalDays),
_ => SH.WebDailyNoteRecoveryTimeDayFormat.Format(totalDays),
};
return string.Format(SH.WebDailyNoteResinRecoveryFormat, day, reach);
return SH.WebDailyNoteResinRecoveryFormat.Format(day, reach);
}
}
@@ -129,9 +129,9 @@ internal sealed class DailyNote : DailyNoteCommon
0 => SH.WebDailyNoteRecoveryTimeDay0,
1 => SH.WebDailyNoteRecoveryTimeDay1,
2 => SH.WebDailyNoteRecoveryTimeDay2,
_ => string.Format(SH.WebDailyNoteRecoveryTimeDayFormat, totalDays),
_ => SH.WebDailyNoteRecoveryTimeDayFormat.Format(totalDays),
};
return string.Format(SH.WebDailyNoteHomeCoinRecoveryFormat, day, reach);
return SH.WebDailyNoteHomeCoinRecoveryFormat.Format(day, reach);
}
}

View File

@@ -84,8 +84,8 @@ internal sealed class Expedition
TimeSpan ts = new(0, 0, RemainedTime);
return ts.Hours > 0
? string.Format(SH.WebDailyNoteExpeditionRemainHoursFormat, ts.Hours)
: string.Format(SH.WebDailyNoteExpeditionRemainMinutesFormat, ts.Minutes);
? SH.WebDailyNoteExpeditionRemainHoursFormat.Format(ts.Hours)
: SH.WebDailyNoteExpeditionRemainMinutesFormat.Format(ts.Minutes);
}
}
}

View File

@@ -67,10 +67,10 @@ internal sealed class RecoveryTime
else
{
return new StringBuilder()
.AppendIf(Day > 0, string.Format(SH.WebDailyNoteTransformerDaysFormat, Day))
.AppendIf(Hour > 0, string.Format(SH.WebDailyNoteTransformerHoursFormat, Hour))
.AppendIf(Minute > 0, string.Format(SH.WebDailyNoteTransformerMinutesFormat, Minute))
.AppendIf(Second > 0, string.Format(SH.WebDailyNoteTransformerSecondsFormat, Second))
.AppendIf(Day > 0, SH.WebDailyNoteTransformerDaysFormat.Format(Day))
.AppendIf(Hour > 0, SH.WebDailyNoteTransformerHoursFormat.Format(Hour))
.AppendIf(Minute > 0, SH.WebDailyNoteTransformerMinutesFormat.Format(Minute))
.AppendIf(Second > 0, SH.WebDailyNoteTransformerSecondsFormat.Format(Second))
.Append(SH.WebDailyNoteTransformerAppend)
.ToString();
}
@@ -92,10 +92,10 @@ internal sealed class RecoveryTime
else
{
return new StringBuilder()
.AppendIf(Day > 0, string.Format(SH.WebDailyNoteTransformerDaysFormat, Day))
.AppendIf(Hour > 0, string.Format(SH.WebDailyNoteTransformerHoursFormat, Hour))
.AppendIf(Minute > 0, string.Format(SH.WebDailyNoteTransformerMinutesFormat, Minute))
.AppendIf(Second > 0, string.Format(SH.WebDailyNoteTransformerSecondsFormat, Second))
.AppendIf(Day > 0, SH.WebDailyNoteTransformerDaysFormat.Format(Day))
.AppendIf(Hour > 0, SH.WebDailyNoteTransformerHoursFormat.Format(Hour))
.AppendIf(Minute > 0, SH.WebDailyNoteTransformerMinutesFormat.Format(Minute))
.AppendIf(Second > 0, SH.WebDailyNoteTransformerSecondsFormat.Format(Second))
.ToString();
}
}

View File

@@ -33,7 +33,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>实时便笺</returns>
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.X4)]
public async Task<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default)
public async ValueTask<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<DailyNote.DailyNote>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
@@ -48,7 +48,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
resp.Message = SH.WebDailyNoteVerificationFailed;
CardVerifier cardVerifier = serviceProvider.GetRequiredService<CardVerifier>();
if (await cardVerifier.TryGetXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is string challenge)
if (await cardVerifier.TryGetXrpcChallengeAsync(userAndUid.User, token).ConfigureAwait(false) is { } challenge)
{
resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
@@ -69,7 +69,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>玩家的基础信息</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.X4)]
public async Task<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
public async ValueTask<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<PlayerInfo>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.LToken)
@@ -88,7 +88,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>深渊信息</returns>
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.X4)]
public async Task<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default)
public async ValueTask<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default)
{
Response<SpiralAbyss.SpiralAbyss>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
@@ -106,7 +106,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>角色基本信息</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.X4)]
public async Task<Response<BasicRoleInfo>> GetRoleBasicInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
public async ValueTask<Response<BasicRoleInfo>> GetRoleBasicInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<BasicRoleInfo>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.LToken)
@@ -125,7 +125,7 @@ internal sealed partial class GameRecordClient : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.X4)]
public async Task<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default)
public async ValueTask<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default)
{
CharacterData data = new(userAndUid.Uid, playerInfo.Avatars.Select(x => x.Id));

View File

@@ -31,7 +31,7 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>实时便笺</returns>
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSX4)]
public async Task<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default)
public async ValueTask<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<DailyNote.DailyNote>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
@@ -49,7 +49,7 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>玩家的基础信息</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSX4)]
public async Task<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
public async ValueTask<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<PlayerInfo>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
@@ -68,7 +68,7 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>深渊信息</returns>
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSX4)]
public async Task<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default)
public async ValueTask<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default)
{
Response<SpiralAbyss.SpiralAbyss>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
@@ -87,7 +87,7 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSX4)]
public async Task<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default)
public async ValueTask<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default)
{
CharacterData data = new(userAndUid.Uid, playerInfo.Avatars.Select(x => x.Id));

View File

@@ -20,7 +20,7 @@ internal interface IGameRecordClient
/// <param name="playerInfo">玩家的基础信息</param>
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
Task<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default);
ValueTask<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default);
/// <summary>
/// 异步获取实时便笺
@@ -28,7 +28,7 @@ internal interface IGameRecordClient
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>实时便笺</returns>
Task<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default);
ValueTask<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default);
/// <summary>
/// 获取玩家基础信息
@@ -36,7 +36,7 @@ internal interface IGameRecordClient
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>玩家的基础信息</returns>
Task<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default);
ValueTask<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default);
/// <summary>
/// 获取玩家深渊信息
@@ -45,5 +45,5 @@ internal interface IGameRecordClient
/// <param name="schedule">1当期2上期</param>
/// <param name="token">取消令牌</param>
/// <returns>深渊信息</returns>
Task<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default);
ValueTask<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default);
}

View File

@@ -17,7 +17,7 @@ internal static class HttpClientExtension
private const string RequestErrorMessage = "请求异常已忽略";
/// <inheritdoc cref="HttpClientJsonExtensions.GetFromJsonAsync{TValue}(HttpClient, string?, JsonSerializerOptions?, CancellationToken)"/>
internal static async Task<T?> TryCatchGetFromJsonAsync<T>(this HttpClient httpClient, string requestUri, JsonSerializerOptions options, ILogger logger, CancellationToken token = default)
internal static async ValueTask<T?> TryCatchGetFromJsonAsync<T>(this HttpClient httpClient, string requestUri, JsonSerializerOptions options, ILogger logger, CancellationToken token = default)
where T : class
{
try
@@ -47,7 +47,7 @@ internal static class HttpClientExtension
}
/// <inheritdoc cref="HttpClientJsonExtensions.PostAsJsonAsync{TValue}(HttpClient, string?, TValue, JsonSerializerOptions?, CancellationToken)"/>
internal static async Task<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, JsonSerializerOptions options, ILogger logger, CancellationToken token = default)
internal static async ValueTask<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, JsonSerializerOptions options, ILogger logger, CancellationToken token = default)
where TResult : class
{
try
@@ -78,7 +78,7 @@ internal static class HttpClientExtension
}
/// <inheritdoc cref="HttpClientJsonExtensions.PostAsJsonAsync{TValue}(HttpClient, string?, TValue, JsonSerializerOptions?, CancellationToken)"/>
internal static async Task<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, CancellationToken token = default)
internal static async ValueTask<TResult?> TryCatchPostAsJsonAsync<TValue, TResult>(this HttpClient httpClient, string requestUri, TValue value, CancellationToken token = default)
where TResult : class
{
try

View File

@@ -40,7 +40,7 @@ internal sealed class HomaGachaLogClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>祈愿统计信息</returns>
public async Task<Response<GachaEventStatistics>> GetGachaEventStatisticsAsync(CancellationToken token = default)
public async ValueTask<Response<GachaEventStatistics>> GetGachaEventStatisticsAsync(CancellationToken token = default)
{
Response<GachaEventStatistics>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<GachaEventStatistics>>(HutaoEndpoints.GachaLogStatisticsCurrentEvents, options, logger, token)
@@ -70,7 +70,7 @@ internal sealed class HomaGachaLogClient
/// <param name="token">取消令牌</param>
/// <returns>Uid 列表</returns>
[Obsolete("Use GetGachaEntriesAsync instead")]
public async Task<Response<List<string>>> GetUidsAsync(CancellationToken token = default)
public async ValueTask<Response<List<string>>> GetUidsAsync(CancellationToken token = default)
{
Response<List<string>>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<List<string>>>(HutaoEndpoints.GachaLogUids, options, logger, token)
@@ -99,7 +99,7 @@ internal sealed class HomaGachaLogClient
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>末尾Id</returns>
public async Task<Response<EndIds>> GetEndIdsAsync(string uid, CancellationToken token = default)
public async ValueTask<Response<EndIds>> GetEndIdsAsync(string uid, CancellationToken token = default)
{
Response<EndIds>? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<EndIds>>(HutaoEndpoints.GachaLogEndIds(uid), options, logger, token)
@@ -115,7 +115,7 @@ internal sealed class HomaGachaLogClient
/// <param name="endIds">末尾 Id</param>
/// <param name="token">取消令牌</param>
/// <returns>云端祈愿记录</returns>
public async Task<Response<List<GachaItem>>> RetrieveGachaItemsAsync(string uid, EndIds endIds, CancellationToken token = default)
public async ValueTask<Response<List<GachaItem>>> RetrieveGachaItemsAsync(string uid, EndIds endIds, CancellationToken token = default)
{
UidAndEndIds uidAndEndIds = new(uid, endIds);
@@ -133,7 +133,7 @@ internal sealed class HomaGachaLogClient
/// <param name="gachaItems">祈愿记录</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async Task<Response.Response> UploadGachaItemsAsync(string uid, List<GachaItem> gachaItems, CancellationToken token = default)
public async ValueTask<Response.Response> UploadGachaItemsAsync(string uid, List<GachaItem> gachaItems, CancellationToken token = default)
{
UidAndItems uidAndItems = new(uid, gachaItems);
@@ -150,7 +150,7 @@ internal sealed class HomaGachaLogClient
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async Task<Response.Response> DeleteGachaItemsAsync(string uid, CancellationToken token = default)
public async ValueTask<Response.Response> DeleteGachaItemsAsync(string uid, CancellationToken token = default)
{
Response.Response? resp = await httpClient
.TryCatchGetFromJsonAsync<Response<List<GachaItem>>>(HutaoEndpoints.GachaLogDelete(uid), options, logger, token)

View File

@@ -33,7 +33,7 @@ internal sealed class HomaLogUploadClient
/// <param name="serviceProvider">服务提供器</param>
/// <param name="exception">异常</param>
/// <returns>任务</returns>
public async Task<string?> UploadLogAsync(IServiceProvider serviceProvider, Exception exception)
public async ValueTask<string?> UploadLogAsync(IServiceProvider serviceProvider, Exception exception)
{
HutaoLog log = BuildFromException(serviceProvider, exception);

View File

@@ -44,7 +44,7 @@ internal sealed partial class HomaPassportClient
/// <param name="isResetPassword">是否重置账号密码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async Task<Response.Response> VerifyAsync(string email, bool isResetPassword, CancellationToken token = default)
public async ValueTask<Response.Response> VerifyAsync(string email, bool isResetPassword, CancellationToken token = default)
{
Dictionary<string, object> data = new()
{
@@ -67,7 +67,7 @@ internal sealed partial class HomaPassportClient
/// <param name="verifyCode">验证码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应,包含登录令牌</returns>
public async Task<Response<string>> RegisterAsync(string email, string password, string verifyCode, CancellationToken token = default)
public async ValueTask<Response<string>> RegisterAsync(string email, string password, string verifyCode, CancellationToken token = default)
{
Dictionary<string, string> data = new()
{
@@ -91,7 +91,7 @@ internal sealed partial class HomaPassportClient
/// <param name="verifyCode">验证码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应,包含登录令牌</returns>
public async Task<Response<string>> ResetPasswordAsync(string email, string password, string verifyCode, CancellationToken token = default)
public async ValueTask<Response<string>> ResetPasswordAsync(string email, string password, string verifyCode, CancellationToken token = default)
{
Dictionary<string, string> data = new()
{
@@ -114,7 +114,7 @@ internal sealed partial class HomaPassportClient
/// <param name="password">密码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应,包含登录令牌</returns>
public async Task<Response<string>> LoginAsync(string email, string password, CancellationToken token = default)
public async ValueTask<Response<string>> LoginAsync(string email, string password, CancellationToken token = default)
{
Dictionary<string, string> data = new()
{
@@ -135,7 +135,7 @@ internal sealed partial class HomaPassportClient
/// <param name="authToken">验证令牌</param>
/// <param name="token">取消令牌</param>
/// <returns>用户信息</returns>
public async Task<Response<UserInfo>> GetUserInfoAsync(string authToken, CancellationToken token = default)
public async ValueTask<Response<UserInfo>> GetUserInfoAsync(string authToken, CancellationToken token = default)
{
httpClient.DefaultRequestHeaders.Authorization = new("Bearer", authToken);

View File

@@ -63,7 +63,7 @@ internal readonly struct QueryString
return new QueryString();
}
int questionMarkIndex = queryString.IndexOf('?');
int questionMarkIndex = queryString.AsSpan().IndexOf('?');
queryString = queryString[(questionMarkIndex + 1)..];
string[] pairs = queryString.Split('&', ';');