support server l10n

This commit is contained in:
DismissedLight
2023-10-25 23:26:46 +08:00
parent ababf74473
commit 9a54463d7d
18 changed files with 495 additions and 97 deletions

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.CodeAnalysis;
using System;
using System.Net.Http;
using System.Runtime.Serialization;
@@ -10,7 +11,18 @@ namespace Snap.Hutao.SourceGeneration.Automation;
[Generator(LanguageNames.CSharp)]
internal sealed class SaltConstantGenerator : IIncrementalGenerator
{
private static readonly HttpClient httpClient = new();
private static readonly HttpClient httpClient;
private static readonly Lazy<Response<SaltLatest>> lazySaltInfo;
static SaltConstantGenerator()
{
httpClient = new();
lazySaltInfo = new Lazy<Response<SaltLatest>>(() =>
{
string body = httpClient.GetStringAsync("https://internal.snapgenshin.cn/Archive/Salt/Latest").GetAwaiter().GetResult();
return JsonParser.FromJson<Response<SaltLatest>>(body)!;
});
}
public void Initialize(IncrementalGeneratorInitializationContext context)
{
@@ -19,8 +31,7 @@ internal sealed class SaltConstantGenerator : IIncrementalGenerator
private static void GenerateSaltContstants(IncrementalGeneratorPostInitializationContext context)
{
string body = httpClient.GetStringAsync("https://internal.snapgenshin.cn/Archive/Salt/Latest").GetAwaiter().GetResult();
Response<SaltLatest> saltInfo = JsonParser.FromJson<Response<SaltLatest>>(body)!;
Response<SaltLatest> saltInfo = lazySaltInfo.Value;
string code = $$"""
namespace Snap.Hutao.Web.Hoyolab;

View File

@@ -50,6 +50,8 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Globalization;
namespace Snap.Hutao.Resource.Localization;
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{nameof(LocalizedEnumGenerator)}}", "1.0.0.0")]
@@ -79,7 +81,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
}
else
{
return SH.ResourceManager.GetString(key);
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
}
}
@@ -102,7 +104,7 @@ internal class LocalizedEnumGenerator : IIncrementalGenerator
_ => string.Empty,
};
return SH.ResourceManager.GetString(key);
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
}
}
""");

View File

@@ -27,6 +27,7 @@ internal sealed partial class ExceptionRecorder
app.DebugSettings.XamlResourceReferenceFailed += OnXamlResourceReferenceFailed;
}
[SuppressMessage("", "CA2012")]
private void OnAppUnhandledException(object? sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
{
ValueTask<string?> task = serviceProvider

View File

@@ -1140,6 +1140,213 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 胡桃云保存的祈愿记录存档数已达当前账号上限 的本地化字符串。
/// </summary>
internal static string ServerGachaLogServiceInsufficientRecordSlot {
get {
return ResourceManager.GetString("ServerGachaLogServiceInsufficientRecordSlot", resourceCulture);
}
}
/// <summary>
/// 查找类似 未开通祈愿记录上传服务或已到期 的本地化字符串。
/// </summary>
internal static string ServerGachaLogServiceInsufficientTime {
get {
return ResourceManager.GetString("ServerGachaLogServiceInsufficientTime", resourceCulture);
}
}
/// <summary>
/// 查找类似 祈愿数据存在无效的物品,无法保存至胡桃云 的本地化字符串。
/// </summary>
internal static string ServerGachaLogServiceInvalidGachaLogData {
get {
return ResourceManager.GetString("ServerGachaLogServiceInvalidGachaLogData", resourceCulture);
}
}
/// <summary>
/// 查找类似 数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试 的本地化字符串。
/// </summary>
internal static string ServerGachaLogServiceServerDatabaseError {
get {
return ResourceManager.GetString("ServerGachaLogServiceServerDatabaseError", resourceCulture);
}
}
/// <summary>
/// 查找类似 当前邮箱尚未注册 的本地化字符串。
/// </summary>
internal static string ServerPassportServiceEmailHasNotRegistered {
get {
return ResourceManager.GetString("ServerPassportServiceEmailHasNotRegistered", resourceCulture);
}
}
/// <summary>
/// 查找类似 当前邮箱已被注册 的本地化字符串。
/// </summary>
internal static string ServerPassportServiceEmailHasRegistered {
get {
return ResourceManager.GetString("ServerPassportServiceEmailHasRegistered", resourceCulture);
}
}
/// <summary>
/// 查找类似 注册失败,服务器异常,请尽快联系开发者解决 的本地化字符串。
/// </summary>
internal static string ServerPassportServiceInternalException {
get {
return ResourceManager.GetString("ServerPassportServiceInternalException", resourceCulture);
}
}
/// <summary>
/// 查找类似 用户不存在,注销失败 的本地化字符串。
/// </summary>
internal static string ServerPassportServiceUnregisterFailed {
get {
return ResourceManager.GetString("ServerPassportServiceUnregisterFailed", resourceCulture);
}
}
/// <summary>
/// 查找类似 用户不存在,获取用户信息失败 的本地化字符串。
/// </summary>
internal static string ServerPassportUserInfoNotExist {
get {
return ResourceManager.GetString("ServerPassportUserInfoNotExist", resourceCulture);
}
}
/// <summary>
/// 查找类似 用户名或密码错误 的本地化字符串。
/// </summary>
internal static string ServerPassportUsernameOrPassportIncorrect {
get {
return ResourceManager.GetString("ServerPassportUsernameOrPassportIncorrect", resourceCulture);
}
}
/// <summary>
/// 查找类似 验证失败 的本地化字符串。
/// </summary>
internal static string ServerPassportVerifyFailed {
get {
return ResourceManager.GetString("ServerPassportVerifyFailed", resourceCulture);
}
}
/// <summary>
/// 查找类似 验证请求过快,请 1 分钟后再试 的本地化字符串。
/// </summary>
internal static string ServerPassportVerifyTooFrequent {
get {
return ResourceManager.GetString("ServerPassportVerifyTooFrequent", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录失败,当前 Uid 已被胡桃数据库封禁 的本地化字符串。
/// </summary>
internal static string ServerRecordBannedUid {
get {
return ResourceManager.GetString("ServerRecordBannedUid", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录失败,正在计算统计数据 的本地化字符串。
/// </summary>
internal static string ServerRecordComputingStatistics {
get {
return ResourceManager.GetString("ServerRecordComputingStatistics", resourceCulture);
}
}
/// <summary>
/// 查找类似 获取数据失败,正在计算统计数据 的本地化字符串。
/// </summary>
internal static string ServerRecordComputingStatistics2 {
get {
return ResourceManager.GetString("ServerRecordComputingStatistics2", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录失败,服务器异常,请尽快联系开发者解决 的本地化字符串。
/// </summary>
internal static string ServerRecordInternalException {
get {
return ResourceManager.GetString("ServerRecordInternalException", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录失败,存在无效的数据 的本地化字符串。
/// </summary>
internal static string ServerRecordInvalidData {
get {
return ResourceManager.GetString("ServerRecordInvalidData", resourceCulture);
}
}
/// <summary>
/// 查找类似 无效的 Uid 的本地化字符串。
/// </summary>
internal static string ServerRecordInvalidUid {
get {
return ResourceManager.GetString("ServerRecordInvalidUid", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录失败,不是本期数据 的本地化字符串。
/// </summary>
internal static string ServerRecordNotCurrentSchedule {
get {
return ResourceManager.GetString("ServerRecordNotCurrentSchedule", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作 的本地化字符串。
/// </summary>
internal static string ServerRecordPreviousRequestNotCompleted {
get {
return ResourceManager.GetString("ServerRecordPreviousRequestNotCompleted", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录成功,获赠祈愿记录上传服务时长 的本地化字符串。
/// </summary>
internal static string ServerRecordUploadSuccessAndGachaLogServiceTimeExtended {
get {
return ResourceManager.GetString("ServerRecordUploadSuccessAndGachaLogServiceTimeExtended", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录成功,但未登录胡桃账号,无法获赠祈愿记录上传服务时长 的本地化字符串。
/// </summary>
internal static string ServerRecordUploadSuccessButNoPassport {
get {
return ResourceManager.GetString("ServerRecordUploadSuccessButNoPassport", resourceCulture);
}
}
/// <summary>
/// 查找类似 上传深渊记录成功,但不是本期首次提交,无法获赠祈愿记录上传服务时长 的本地化字符串。
/// </summary>
internal static string ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule {
get {
return ResourceManager.GetString("ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule", resourceCulture);
}
}
/// <summary>
/// 查找类似 新增:{0} 个成就 | 更新:{1} 个成就 | 删除:{2} 个成就 的本地化字符串。
/// </summary>

View File

@@ -506,6 +506,75 @@
<data name="MustSelectUserAndUid" xml:space="preserve">
<value>必须先选择一个用户与角色</value>
</data>
<data name="ServerGachaLogServiceInsufficientRecordSlot" xml:space="preserve">
<value>胡桃云保存的祈愿记录存档数已达当前账号上限</value>
</data>
<data name="ServerGachaLogServiceInsufficientTime" xml:space="preserve">
<value>未开通祈愿记录上传服务或已到期</value>
</data>
<data name="ServerGachaLogServiceInvalidGachaLogData" xml:space="preserve">
<value>祈愿数据存在无效的物品,无法保存至胡桃云</value>
</data>
<data name="ServerGachaLogServiceServerDatabaseError" xml:space="preserve">
<value>数据异常,无法保存至云端,请勿跨账号上传或尝试删除云端数据后重试</value>
</data>
<data name="ServerPassportServiceEmailHasNotRegistered" xml:space="preserve">
<value>当前邮箱尚未注册</value>
</data>
<data name="ServerPassportServiceEmailHasRegistered" xml:space="preserve">
<value>当前邮箱已被注册</value>
</data>
<data name="ServerPassportServiceInternalException" xml:space="preserve">
<value>注册失败,服务器异常,请尽快联系开发者解决</value>
</data>
<data name="ServerPassportServiceUnregisterFailed" xml:space="preserve">
<value>用户不存在,注销失败</value>
</data>
<data name="ServerPassportUserInfoNotExist" xml:space="preserve">
<value>用户不存在,获取用户信息失败</value>
</data>
<data name="ServerPassportUsernameOrPassportIncorrect" xml:space="preserve">
<value>用户名或密码错误</value>
</data>
<data name="ServerPassportVerifyFailed" xml:space="preserve">
<value>验证失败</value>
</data>
<data name="ServerPassportVerifyTooFrequent" xml:space="preserve">
<value>验证请求过快,请 1 分钟后再试</value>
</data>
<data name="ServerRecordBannedUid" xml:space="preserve">
<value>上传深渊记录失败,当前 Uid 已被胡桃数据库封禁</value>
</data>
<data name="ServerRecordComputingStatistics" xml:space="preserve">
<value>上传深渊记录失败,正在计算统计数据</value>
</data>
<data name="ServerRecordComputingStatistics2" xml:space="preserve">
<value>获取数据失败,正在计算统计数据</value>
</data>
<data name="ServerRecordInternalException" xml:space="preserve">
<value>上传深渊记录失败,服务器异常,请尽快联系开发者解决</value>
</data>
<data name="ServerRecordInvalidData" xml:space="preserve">
<value>上传深渊记录失败,存在无效的数据</value>
</data>
<data name="ServerRecordInvalidUid" xml:space="preserve">
<value>无效的 Uid</value>
</data>
<data name="ServerRecordNotCurrentSchedule" xml:space="preserve">
<value>上传深渊记录失败,不是本期数据</value>
</data>
<data name="ServerRecordPreviousRequestNotCompleted" xml:space="preserve">
<value>上传深渊记录失败,当前 Uid 的记录仍在处理中,请勿重复操作</value>
</data>
<data name="ServerRecordUploadSuccessAndGachaLogServiceTimeExtended" xml:space="preserve">
<value>上传深渊记录成功,获赠祈愿记录上传服务时长</value>
</data>
<data name="ServerRecordUploadSuccessButNoPassport" xml:space="preserve">
<value>上传深渊记录成功,但未登录胡桃账号,无法获赠祈愿记录上传服务时长</value>
</data>
<data name="ServerRecordUploadSuccessButNotFirstTimeAtCurrentSchedule" xml:space="preserve">
<value>上传深渊记录成功,但不是本期首次提交,无法获赠祈愿记录上传服务时长</value>
</data>
<data name="ServiceAchievementImportResultFormat" xml:space="preserve">
<value>新增:{0} 个成就 | 更新:{1} 个成就 | 删除:{2} 个成就</value>
</data>

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using Snap.Hutao.Web.Response;
@@ -54,7 +55,7 @@ internal readonly struct TypedWishSummaryBuilderContext
return new(taskContext, gachaLogClient, SH.ServiceGachaLogFactoryWeaponWishName, 80, 10, IsWeaponEventWish, GachaDistributionType.WeaponEvent);
}
public ValueTask<Response<GachaDistribution>> GetGachaDistributionAsync()
public ValueTask<HutaoResponse<GachaDistribution>> GetGachaDistributionAsync()
{
return GachaLogClient.GetGachaDistributionAsync(DistributionType);
}

View File

@@ -10,6 +10,7 @@ using Snap.Hutao.Service.GachaLog.Factory;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using Snap.Hutao.Web.Response;
@@ -27,7 +28,7 @@ internal sealed partial class GachaLogHutaoCloudService : IGachaLogHutaoCloudSer
private readonly IGachaLogDbService gachaLogDbService;
/// <inheritdoc/>
public ValueTask<Response<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default)
public ValueTask<HutaoResponse<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default)
{
return homaGachaLogClient.GetGachaEntriesAsync(token);
}

View File

@@ -3,6 +3,7 @@
using Snap.Hutao.Model.Entity;
using Snap.Hutao.ViewModel.GachaLog;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.GachaLog;
using Snap.Hutao.Web.Response;
@@ -28,7 +29,7 @@ internal interface IGachaLogHutaoCloudService
/// <returns>祈愿统计信息</returns>
ValueTask<ValueResult<bool, HutaoStatistics>> GetCurrentEventStatisticsAsync(CancellationToken token = default);
ValueTask<Response<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default);
ValueTask<HutaoResponse<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default);
/// <summary>
/// 异步获取祈愿记录

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.Extensions.Caching.Memory;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.SpiralAbyss;
using Snap.Hutao.Web.Response;
@@ -64,7 +65,7 @@ internal sealed partial class HutaoSpiralAbyssService : IHutaoSpiralAbyssService
return FromCacheOrWebAsync(nameof(TeamAppearance), homaClient.GetTeamCombinationsAsync);
}
private async ValueTask<T> FromCacheOrWebAsync<T>(string typeName, Func<CancellationToken, ValueTask<Response<T>>> taskFunc)
private async ValueTask<T> FromCacheOrWebAsync<T>(string typeName, Func<CancellationToken, ValueTask<HutaoResponse<T>>> taskFunc)
where T : class, new()
{
string key = $"{nameof(HutaoSpiralAbyssService)}.Cache.{typeName}";

View File

@@ -8,6 +8,7 @@ using Snap.Hutao.Service.SpiralAbyss;
using Snap.Hutao.Service.User;
using Snap.Hutao.ViewModel.Complex;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hutao;
using Snap.Hutao.Web.Hutao.SpiralAbyss;
using Snap.Hutao.Web.Hutao.SpiralAbyss.Post;
using System.Collections.ObjectModel;
@@ -134,11 +135,17 @@ internal sealed partial class SpiralAbyssRecordViewModel : Abstraction.ViewModel
if (response is { ReturnCode: 0 })
{
infoBarService.Success(response.Message);
if (response is ILocalizableResponse localizableResponse)
{
infoBarService.Success(localizableResponse.GetLocalizationMessage());
}
}
else
{
infoBarService.Warning(response.Message);
if (response is ILocalizableResponse localizableResponse)
{
infoBarService.Warning(localizableResponse.GetLocalizationMessage());
}
}
}
}

View File

@@ -148,5 +148,4 @@ internal sealed class SpiralAbyssView : IEntityOnly<SpiralAbyssEntry?>,
{
return ranks.Where(r => r.AvatarId != 0U).Select(r => new RankAvatar(r.Value, context.IdAvatarMap[r.AvatarId])).SingleOrDefault();
}
}

View File

@@ -27,7 +27,7 @@ internal sealed partial class HomaGachaLogClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>祈愿统计信息</returns>
public async ValueTask<Response<GachaEventStatistics>> GetGachaEventStatisticsAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<GachaEventStatistics>> GetGachaEventStatisticsAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogStatisticsCurrentEvents)
@@ -35,11 +35,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response<GachaEventStatistics>? resp = await builder
.TryCatchSendAsync<Response<GachaEventStatistics>>(httpClient, logger, token)
HutaoResponse<GachaEventStatistics>? resp = await builder
.TryCatchSendAsync<HutaoResponse<GachaEventStatistics>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -48,7 +48,7 @@ internal sealed partial class HomaGachaLogClient
/// <param name="distributionType">分布类型</param>
/// <param name="token">取消令牌</param>
/// <returns>祈愿分布</returns>
public async ValueTask<Response<GachaDistribution>> GetGachaDistributionAsync(GachaDistributionType distributionType, CancellationToken token = default)
public async ValueTask<HutaoResponse<GachaDistribution>> GetGachaDistributionAsync(GachaDistributionType distributionType, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogStatisticsDistribution(distributionType))
@@ -56,11 +56,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response<GachaDistribution>? resp = await builder
.TryCatchSendAsync<Response<GachaDistribution>>(httpClient, logger, token)
HutaoResponse<GachaDistribution>? resp = await builder
.TryCatchSendAsync<HutaoResponse<GachaDistribution>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -68,7 +68,7 @@ internal sealed partial class HomaGachaLogClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>Uid 列表</returns>
public async ValueTask<Response<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<GachaEntry>>> GetGachaEntriesAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogEntries)
@@ -76,11 +76,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response<List<GachaEntry>>? resp = await builder
.TryCatchSendAsync<Response<List<GachaEntry>>>(httpClient, logger, token)
HutaoResponse<List<GachaEntry>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<GachaEntry>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -89,7 +89,7 @@ internal sealed partial class HomaGachaLogClient
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>末尾Id</returns>
public async ValueTask<Response<EndIds>> GetEndIdsAsync(string uid, CancellationToken token = default)
public async ValueTask<HutaoResponse<EndIds>> GetEndIdsAsync(string uid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogEndIds(uid))
@@ -97,11 +97,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response<EndIds>? resp = await builder
.TryCatchSendAsync<Response<EndIds>>(httpClient, logger, token)
HutaoResponse<EndIds>? resp = await builder
.TryCatchSendAsync<HutaoResponse<EndIds>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -111,7 +111,7 @@ internal sealed partial class HomaGachaLogClient
/// <param name="endIds">末尾 Id</param>
/// <param name="token">取消令牌</param>
/// <returns>云端祈愿记录</returns>
public async ValueTask<Response<List<GachaItem>>> RetrieveGachaItemsAsync(string uid, EndIds endIds, CancellationToken token = default)
public async ValueTask<HutaoResponse<List<GachaItem>>> RetrieveGachaItemsAsync(string uid, EndIds endIds, CancellationToken token = default)
{
UidAndEndIds uidAndEndIds = new(uid, endIds);
@@ -121,11 +121,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response<List<GachaItem>>? resp = await builder
.TryCatchSendAsync<Response<List<GachaItem>>>(httpClient, logger, token)
HutaoResponse<List<GachaItem>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<GachaItem>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -135,7 +135,7 @@ internal sealed partial class HomaGachaLogClient
/// <param name="gachaItems">祈愿记录</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async ValueTask<Response.Response> UploadGachaItemsAsync(string uid, List<GachaItem> gachaItems, CancellationToken token = default)
public async ValueTask<HutaoResponse> UploadGachaItemsAsync(string uid, List<GachaItem> gachaItems, CancellationToken token = default)
{
UidAndItems uidAndItems = new(uid, gachaItems);
@@ -145,11 +145,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response.Response? resp = await builder
.TryCatchSendAsync<Response.Response>(httpClient, logger, token)
HutaoResponse? resp = await builder
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -158,7 +158,7 @@ internal sealed partial class HomaGachaLogClient
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async ValueTask<Response.Response> DeleteGachaItemsAsync(string uid, CancellationToken token = default)
public async ValueTask<HutaoResponse> DeleteGachaItemsAsync(string uid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogDelete(uid))
@@ -166,11 +166,11 @@ internal sealed partial class HomaGachaLogClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response.Response? resp = await builder
.TryCatchSendAsync<Response.Response>(httpClient, logger, token)
HutaoResponse? resp = await builder
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
private sealed class UidAndEndIds

View File

@@ -24,20 +24,20 @@ internal sealed partial class HutaoAsAServiceClient
private readonly MetadataOptions metadataOptions;
private readonly HttpClient httpClient;
public async ValueTask<Response<List<Announcement>>> GetAnnouncementListAsync(List<long> excluedeIds, CancellationToken token = default)
public async ValueTask<HutaoResponse<List<Announcement>>> GetAnnouncementListAsync(List<long> excluedeIds, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.Announcement(metadataOptions.LocaleName))
.PostJson(excluedeIds);
Response<List<Announcement>>? resp = await builder
.TryCatchSendAsync<Response<List<Announcement>>>(httpClient, logger, token)
HutaoResponse<List<Announcement>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<Announcement>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
public async ValueTask<Response.Response> UploadAnnouncementAsync(UploadAnnouncement uploadAnnouncement, CancellationToken token = default)
public async ValueTask<HutaoResponse> UploadAnnouncementAsync(UploadAnnouncement uploadAnnouncement, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.AnnouncementUpload)
@@ -45,14 +45,14 @@ internal sealed partial class HutaoAsAServiceClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response.Response? resp = await builder
.TryCatchSendAsync<Response.Response>(httpClient, logger, token)
HutaoResponse? resp = await builder
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
public async ValueTask<Response.Response> GachaLogCompensationAsync(int days, CancellationToken token = default)
public async ValueTask<HutaoResponse> GachaLogCompensationAsync(int days, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogCompensation(days))
@@ -60,14 +60,14 @@ internal sealed partial class HutaoAsAServiceClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response.Response? resp = await builder
.TryCatchSendAsync<Response.Response>(httpClient, logger, token)
HutaoResponse? resp = await builder
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
public async ValueTask<Response.Response> GachaLogDesignationAsync(string userName, int days, CancellationToken token = default)
public async ValueTask<HutaoResponse> GachaLogDesignationAsync(string userName, int days, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.GachaLogDesignation(userName, days))
@@ -75,10 +75,10 @@ internal sealed partial class HutaoAsAServiceClient
await builder.TrySetTokenAsync(hutaoUserOptions).ConfigureAwait(false);
Response.Response? resp = await builder
.TryCatchSendAsync<Response.Response>(httpClient, logger, token)
HutaoResponse? resp = await builder
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
}

View File

@@ -0,0 +1,58 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Service.Notification;
using System.Runtime.CompilerServices;
namespace Snap.Hutao.Web.Hutao;
internal sealed class HutaoResponse : Response.Response, ILocalizableResponse
{
[JsonConstructor]
public HutaoResponse(int returnCode, string message, string? localizationKey)
: base(returnCode, message)
{
LocalizationKey = localizationKey;
}
[JsonPropertyName("l10nKey")]
public string? LocalizationKey { get; set; }
public static HutaoResponse DefaultIfNull(HutaoResponse? response, [CallerMemberName] string callerName = default!)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
response ??= new(InternalFailure, SH.WebResponseRequestExceptionFormat.Format(callerName, null), default);
return response;
}
public static HutaoResponse<TData> DefaultIfNull<TData>(HutaoResponse<TData>? response, [CallerMemberName] string callerName = default!)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
response ??= new(InternalFailure, SH.WebResponseRequestExceptionFormat.Format(callerName, typeof(TData).Name), default, default);
return response ?? new(InternalFailure, SH.WebResponseRequestExceptionFormat.Format(callerName, typeof(TData).Name), default, default);
}
public override string ToString()
{
return SH.WebResponseFormat.Format(ReturnCode, this.GetLocalizationMessageOrDefault());
}
}
[SuppressMessage("", "SA1402")]
internal sealed class HutaoResponse<TData> : Response.Response<TData>, ILocalizableResponse
{
[JsonConstructor]
public HutaoResponse(int returnCode, string message, TData? data, string? localizationKey)
: base(returnCode, message, data)
{
LocalizationKey = localizationKey;
}
[JsonPropertyName("l10nKey")]
public string? LocalizationKey { get; set; }
public override string ToString()
{
return SH.WebResponseFormat.Format(ReturnCode, this.GetLocalizationMessageOrDefault());
}
}

View File

@@ -0,0 +1,9 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hutao;
internal interface ILocalizableResponse
{
public string? LocalizationKey { get; set; }
}

View File

@@ -0,0 +1,31 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System.Globalization;
namespace Snap.Hutao.Web.Hutao;
internal static class LocalizableResponseExtension
{
public static string? GetLocalizationMessageOrDefault(this ILocalizableResponse localizableResponse)
{
string? key = localizableResponse.LocalizationKey;
if (string.IsNullOrEmpty(key))
{
return default;
}
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture);
}
public static string GetLocalizationMessage(this ILocalizableResponse localizableResponse)
{
string? key = localizableResponse.LocalizationKey;
if (string.IsNullOrEmpty(key))
{
return string.Empty;
}
return SH.ResourceManager.GetString(key, CultureInfo.CurrentCulture) ?? string.Empty;
}
}

View File

@@ -36,17 +36,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>当前是否上传了数据</returns>
public async ValueTask<Response<bool>> CheckRecordUploadedAsync(PlayerUid uid, CancellationToken token = default)
public async ValueTask<HutaoResponse<bool>> CheckRecordUploadedAsync(PlayerUid uid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.RecordCheck(uid.Value))
.Get();
Response<bool>? resp = await builder
.TryCatchSendAsync<Response<bool>>(httpClient, logger, token)
HutaoResponse<bool>? resp = await builder
.TryCatchSendAsync<HutaoResponse<bool>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -56,17 +56,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// <param name="uid">uid</param>
/// <param name="token">取消令牌</param>
/// <returns>排行信息</returns>
public async ValueTask<Response<RankInfo>> GetRankAsync(PlayerUid uid, CancellationToken token = default)
public async ValueTask<HutaoResponse<RankInfo>> GetRankAsync(PlayerUid uid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.RecordRank(uid.Value))
.Get();
Response<RankInfo>? resp = await builder
.TryCatchSendAsync<Response<RankInfo>>(httpClient, logger, token)
HutaoResponse<RankInfo>? resp = await builder
.TryCatchSendAsync<HutaoResponse<RankInfo>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -75,17 +75,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>总览信息</returns>
public async ValueTask<Response<Overview>> GetOverviewAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<Overview>> GetOverviewAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsOverview)
.Get();
Response<Overview>? resp = await builder
.TryCatchSendAsync<Response<Overview>>(httpClient, logger, token)
HutaoResponse<Overview>? resp = await builder
.TryCatchSendAsync<HutaoResponse<Overview>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -94,17 +94,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>角色出场率</returns>
public async ValueTask<Response<List<AvatarAppearanceRank>>> GetAvatarAttendanceRatesAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<AvatarAppearanceRank>>> GetAvatarAttendanceRatesAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsAvatarAttendanceRate)
.Get();
Response<List<AvatarAppearanceRank>>? resp = await builder
.TryCatchSendAsync<Response<List<AvatarAppearanceRank>>>(httpClient, logger, token)
HutaoResponse<List<AvatarAppearanceRank>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<AvatarAppearanceRank>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -113,17 +113,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>角色出场率</returns>
public async ValueTask<Response<List<AvatarUsageRank>>> GetAvatarUtilizationRatesAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<AvatarUsageRank>>> GetAvatarUtilizationRatesAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsAvatarUtilizationRate)
.Get();
Response<List<AvatarUsageRank>>? resp = await builder
.TryCatchSendAsync<Response<List<AvatarUsageRank>>>(httpClient, logger, token)
HutaoResponse<List<AvatarUsageRank>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<AvatarUsageRank>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -132,17 +132,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>角色/武器/圣遗物搭配</returns>
public async ValueTask<Response<List<AvatarCollocation>>> GetAvatarCollocationsAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<AvatarCollocation>>> GetAvatarCollocationsAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsAvatarAvatarCollocation)
.Get();
Response<List<AvatarCollocation>>? resp = await builder
.TryCatchSendAsync<Response<List<AvatarCollocation>>>(httpClient, logger, token)
HutaoResponse<List<AvatarCollocation>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<AvatarCollocation>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -151,17 +151,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>角色/武器/圣遗物搭配</returns>
public async ValueTask<Response<List<WeaponCollocation>>> GetWeaponCollocationsAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<WeaponCollocation>>> GetWeaponCollocationsAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsWeaponWeaponCollocation)
.Get();
Response<List<WeaponCollocation>>? resp = await builder
.TryCatchSendAsync<Response<List<WeaponCollocation>>>(httpClient, logger, token)
HutaoResponse<List<WeaponCollocation>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<WeaponCollocation>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -170,17 +170,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>角色图片列表</returns>
public async ValueTask<Response<List<AvatarConstellationInfo>>> GetAvatarHoldingRatesAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<AvatarConstellationInfo>>> GetAvatarHoldingRatesAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsAvatarHoldingRate)
.Get();
Response<List<AvatarConstellationInfo>>? resp = await builder
.TryCatchSendAsync<Response<List<AvatarConstellationInfo>>>(httpClient, logger, token)
HutaoResponse<List<AvatarConstellationInfo>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<AvatarConstellationInfo>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -189,17 +189,17 @@ internal sealed partial class HomaSpiralAbyssClient
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>队伍出场列表</returns>
public async ValueTask<Response<List<TeamAppearance>>> GetTeamCombinationsAsync(CancellationToken token = default)
public async ValueTask<HutaoResponse<List<TeamAppearance>>> GetTeamCombinationsAsync(CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.StatisticsTeamCombination)
.Get();
Response<List<TeamAppearance>>? resp = await builder
.TryCatchSendAsync<Response<List<TeamAppearance>>>(httpClient, logger, token)
HutaoResponse<List<TeamAppearance>>? resp = await builder
.TryCatchSendAsync<HutaoResponse<List<TeamAppearance>>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
/// <summary>
@@ -248,16 +248,16 @@ internal sealed partial class HomaSpiralAbyssClient
/// <param name="playerRecord">玩家记录</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async ValueTask<Response.Response> UploadRecordAsync(SimpleRecord playerRecord, CancellationToken token = default)
public async ValueTask<HutaoResponse> UploadRecordAsync(SimpleRecord playerRecord, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(HutaoEndpoints.RecordUpload)
.PostJson(playerRecord);
Response.Response? resp = await builder
.TryCatchSendAsync<Response.Response>(httpClient, logger, token)
HutaoResponse? resp = await builder
.TryCatchSendAsync<HutaoResponse>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
return HutaoResponse.DefaultIfNull(resp);
}
}

View File

@@ -139,7 +139,7 @@ internal class Response
/// <typeparam name="TData">数据类型</typeparam>
[SuppressMessage("", "SA1402")]
[HighQuality]
internal sealed class Response<TData> : Response, IJsResult
internal class Response<TData> : Response, IJsResult
{
/// <summary>
/// 构造一个新的 Mihoyo 标准API响应