update banner

This commit is contained in:
DismissedLight
2023-03-23 22:24:19 +08:00
parent 2ba6377088
commit b550b91cc9
14 changed files with 309 additions and 18 deletions

View File

@@ -1,4 +1,4 @@
![](https://repository-images.githubusercontent.com/482734649/5f8cf574-2ef0-43e9-aa8d-6cf094b54dd9)
![](https://repository-images.githubusercontent.com/482734649/c47a0a8e-868d-4d07-a66f-6d4473abfe46)
## 下载使用
[<img src="https://get.microsoft.com/images/zh-cn%20light.svg" width="30%" height="30%">](https://apps.microsoft.com/store/detail/snap-hutao/9PH4NXJ2JN52)

BIN
res/HutaoRepoBanner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 699 KiB

BIN
res/HutaoRepoBanner.psd Normal file

Binary file not shown.

View File

@@ -29,6 +29,16 @@ internal static class SettingKeys
/// </summary>
public const string DataFolderPath = "DataFolderPath";
/// <summary>
/// 通行证用户名(邮箱)
/// </summary>
public const string PassportUserName = "PassportUserName";
/// <summary>
/// 通行证密码
/// </summary>
public const string PassportPassword = "PassportPassword";
/// <summary>
/// 静态资源合约
/// 新增合约时 请注意

View File

@@ -4281,6 +4281,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 胡桃账号 的本地化字符串。
/// </summary>
internal static string ViewPageSettingHutaoPassportHeader {
get {
return ResourceManager.GetString("ViewPageSettingHutaoPassportHeader", resourceCulture);
}
}
/// <summary>
/// 查找类似 在完整阅读原神和胡桃工具箱用户协议后,我选择启用「启动游戏-高级功能」 的本地化字符串。
/// </summary>
@@ -4488,6 +4497,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
/// <summary>
/// 查找类似 登录失败,请重新登录 的本地化字符串。
/// </summary>
internal static string ViewServiceHutaoUserLoginFailHint {
get {
return ResourceManager.GetString("ViewServiceHutaoUserLoginFailHint", resourceCulture);
}
}
/// <summary>
/// 查找类似 立即登录或注册 的本地化字符串。
/// </summary>
internal static string ViewServiceHutaoUserLoginOrRegisterHint {
get {
return ResourceManager.GetString("ViewServiceHutaoUserLoginOrRegisterHint", resourceCulture);
}
}
/// <summary>
/// 查找类似 战斗数据 的本地化字符串。
/// </summary>

View File

@@ -1797,4 +1797,13 @@
<data name="ViewDialogSettingDeleteUserDataTitle" xml:space="preserve">
<value>是否永久删除用户数据</value>
</data>
<data name="ViewPageSettingHutaoPassportHeader" xml:space="preserve">
<value>胡桃账号</value>
</data>
<data name="ViewServiceHutaoUserLoginFailHint" xml:space="preserve">
<value>登录失败,请重新登录</value>
</data>
<data name="ViewServiceHutaoUserLoginOrRegisterHint" xml:space="preserve">
<value>立即登录或注册</value>
</data>
</root>

View File

@@ -103,18 +103,21 @@ internal sealed class HutaoService : IHutaoService
}
Response<T> webResponse = await taskFunc(default).ConfigureAwait(false);
T web = webResponse.IsOk() ? webResponse.Data : new();
T? data = webResponse.Data;
try
{
appDbContext.ObjectCache.AddAndSave(new()
if (data != null)
{
Key = key,
appDbContext.ObjectCache.AddAndSave(new()
{
Key = key,
// we hold the cache for 4 hours, then just expire it.
ExpireTime = DateTimeOffset.Now.AddHours(4),
Value = JsonSerializer.Serialize(web, options),
});
// we hold the cache for 4 hours, then just expire it.
ExpireTime = DateTimeOffset.Now.AddHours(4),
Value = JsonSerializer.Serialize(data, options),
});
}
}
catch (Microsoft.EntityFrameworkCore.DbUpdateException)
{
@@ -122,6 +125,6 @@ internal sealed class HutaoService : IHutaoService
// TODO: Not ignore it.
}
return memoryCache.Set(key, web, TimeSpan.FromMinutes(30));
return memoryCache.Set(key, data ?? new(), TimeSpan.FromHours(4));
}
}
}

View File

@@ -0,0 +1,77 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.Setting;
using Snap.Hutao.Web.Hutao;
namespace Snap.Hutao.Service.Hutao;
/// <summary>
/// 胡桃用户服务
/// </summary>
[Injection(InjectAs.Singleton, typeof(IHutaoUserService))]
internal sealed class HutaoUserService : IHutaoUserService, IHutaoUserServiceInitialization
{
private readonly HomaPassportClient passportClient;
private readonly TaskCompletionSource initializeCompletionSource = new();
private bool isInitialized;
/// <summary>
/// 构造一个新的胡桃用户服务
/// </summary>
/// <param name="passportClient">通行证客户端</param>
public HutaoUserService(HomaPassportClient passportClient)
{
this.passportClient = passportClient;
}
/// <summary>
/// 用户名
/// </summary>
public string? UserName { get; private set; }
/// <summary>
/// 访问令牌
/// </summary>
public string? Token { get; private set; }
/// <summary>
/// 异步初始化
/// </summary>
/// <returns>任务</returns>
public async ValueTask<bool> InitializeAsync()
{
await initializeCompletionSource.Task.ConfigureAwait(false);
return isInitialized;
}
/// <inheritdoc/>
public async Task InitializeInternalAsync(CancellationToken token = default)
{
string userName = LocalSetting.Get(SettingKeys.PassportUserName, string.Empty);
string passport = LocalSetting.Get(SettingKeys.PassportPassword, string.Empty);
if (!string.IsNullOrEmpty(userName) && !string.IsNullOrEmpty(passport))
{
Web.Response.Response<string> response = await passportClient.LoginAsync(userName, passport, token).ConfigureAwait(false);
if (response.IsOk())
{
Token = response.Data;
UserName = userName;
isInitialized = true;
}
else
{
UserName = SH.ViewServiceHutaoUserLoginFailHint;
}
}
else
{
UserName = SH.ViewServiceHutaoUserLoginOrRegisterHint;
}
initializeCompletionSource.TrySetResult();
}
}

View File

@@ -0,0 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Hutao;
/// <summary>
/// 胡桃用户服务
/// </summary>
internal interface IHutaoUserService : ICastableService
{
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Service.Hutao;
/// <summary>
/// 指示该类为用户服务初始化器
/// </summary>
internal interface IHutaoUserServiceInitialization
{
/// <summary>
/// 异步初始化
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>任务</returns>
Task InitializeInternalAsync(CancellationToken token = default);
}

View File

@@ -72,6 +72,10 @@
Description="{Binding AppVersion}"
Header="{shcm:ResourceString Name=AppName}"
HeaderIcon="{shcm:FontIcon Glyph=&#xECAA;}"/>
<clw:SettingsCard
Description="{Binding AppVersion}"
Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xE716;}"/>
<clw:SettingsCard
ActionIcon="{shcm:FontIcon Glyph=&#xE8C8;}"
ActionIconToolTip="{shcm:ResourceString Name=ViewPageSettingCopyDeviceIdAction}"
@@ -199,15 +203,9 @@
IsOpen="{Binding Options.IsAdvancedLaunchOptionsEnabled}"
Message="{shcm:ResourceString Name=ViewPageSettingFeaturesDangerousHint}"
Severity="Error"/>
<InfoBar
IsClosable="False"
IsOpen="{Binding IsElevated, Converter={StaticResource BoolNegationConverter}}"
Message="{shcm:ResourceString Name=ViewPageLaunchGameElevationHint}"
Severity="Warning"/>
<clw:SettingsCard
Background="{StaticResource SystemFillColorCriticalBackgroundBrush}"
BorderBrush="{StaticResource SystemFillColorCriticalBrush}"
Description="{shcm:ResourceString Name=ViewPageSettingIsAdvancedLaunchOptionsEnabledDescription}"
Header="{shcm:ResourceString Name=ViewPageSettingIsAdvancedLaunchOptionsEnabledHeader}"
HeaderIcon="{shcm:FontIcon Glyph=&#xE730;}">
@@ -217,6 +215,7 @@
IsOn="{Binding Options.IsAdvancedLaunchOptionsEnabled, Mode=TwoWay}"/>
</clw:SettingsCard>
<InfoBar
Margin="0,4,0,0"
IsClosable="False"
IsOpen="True"
Message="{shcm:ResourceString Name=ViewPageSettingDangerousHint}"

View File

@@ -2,9 +2,10 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Web.Hutao.Log;
using Snap.Hutao.Web.Response;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
namespace Snap.Hutao.Web.Hutao;
@@ -15,6 +16,21 @@ namespace Snap.Hutao.Web.Hutao;
[HttpClient(HttpClientConfigration.Default)]
internal sealed class HomaPassportClient
{
/// <summary>
/// 通行证请求公钥
/// </summary>
public const string PublicKey = """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5W2SEyZSlP2zBI1Sn8Gd
TwbZoXlUGNKyoVrY8SVYu9GMefdGZCrUQNkCG/Np8pWPmSSEFGd5oeug/oIMtCZQ
NOn0drlR+pul/XZ1KQhKmj/arWjN1XNok2qXF7uxhqD0JyNT/Fxy6QvzqIpBsM9S
7ajm8/BOGlPG1SInDPaqTdTRTT30AuN+IhWEEFwT3Ctv1SmDupHs2Oan5qM7Y3uw
b6K1rbnk5YokiV2FzHajGUymmSKXqtG1USZzwPqImpYb4Z0M/StPFWdsKqexBqMM
mkXckI5O98GdlszEmQ0Ejv5Fx9fR2rXRwM76S4iZTfabYpiMbb4bM42mHMauupj6
9QIDAQAB
-----END PUBLIC KEY-----
""";
private readonly HttpClient httpClient;
/// <summary>
@@ -30,9 +46,102 @@ internal sealed class HomaPassportClient
/// 异步获取验证码
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="isResetPassword">是否重置账号密码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
public async Task<Response.Response> VerifyAsync(string email)
public async Task<Response.Response> VerifyAsync(string email, bool isResetPassword, CancellationToken token = default)
{
Dictionary<string, object> data = new()
{
["UserName"] = Encrypt(email),
["IsResetPassword"] = isResetPassword,
};
Response.Response? resp = await httpClient
.TryCatchPostAsJsonAsync<Dictionary<string, object>, Response.Response>(HutaoEndpoints.PassportVerify, data, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
/// <summary>
/// 异步注册
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="password">密码</param>
/// <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)
{
Dictionary<string, string> data = new()
{
["UserName"] = Encrypt(email),
["Password"] = Encrypt(password),
["VerifyCode"] = Encrypt(verifyCode),
};
Response<string>? resp = await httpClient
.TryCatchPostAsJsonAsync<Dictionary<string, string>, Response<string>>(HutaoEndpoints.PassportRegister, data, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
/// <summary>
/// 异步重置密码
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="password">密码</param>
/// <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)
{
Dictionary<string, string> data = new()
{
["UserName"] = Encrypt(email),
["Password"] = Encrypt(password),
["VerifyCode"] = Encrypt(verifyCode),
};
Response<string>? resp = await httpClient
.TryCatchPostAsJsonAsync<Dictionary<string, string>, Response<string>>(HutaoEndpoints.PassportResetPassword, data, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
/// <summary>
/// 异步登录
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="password">密码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应,包含登录令牌</returns>
public async Task<Response<string>> LoginAsync(string email, string password, CancellationToken token = default)
{
Dictionary<string, string> data = new()
{
["UserName"] = Encrypt(email),
["Password"] = Encrypt(password),
};
Response<string>? resp = await httpClient
.TryCatchPostAsJsonAsync<Dictionary<string, string>, Response<string>>(HutaoEndpoints.PassportLogin, data, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
private static string Encrypt(string text)
{
byte[] plaintextBytes = Encoding.UTF8.GetBytes(text);
using (RSACryptoServiceProvider rsa = new(2048))
{
rsa.ImportFromPem(PublicKey);
byte[] encryptedBytes = rsa.Encrypt(plaintextBytes, true);
return Convert.ToBase64String(encryptedBytes);
}
}
}

View File

@@ -16,6 +16,29 @@ internal static class HutaoEndpoints
/// </summary>
public const string StaticHutao = "static.hut.ao";
#region Passport
/// <summary>
/// 获取注册验证码
/// </summary>
public const string PassportVerify = $"{HomaSnapGenshinApi}/Passport/Verify";
/// <summary>
/// 注册账号
/// </summary>
public const string PassportRegister = $"{HomaSnapGenshinApi}/Passport/Register";
/// <summary>
/// 重设密码
/// </summary>
public const string PassportResetPassword = $"{HomaSnapGenshinApi}/Passport/ResetPassword";
/// <summary>
/// 登录
/// </summary>
public const string PassportLogin = $"{HomaSnapGenshinApi}/Passport/Login";
#endregion
#region HutaoAPI
/// <summary>

View File

@@ -40,6 +40,12 @@ internal class Response
[JsonPropertyName("message")]
public string Message { get; set; } = default!;
public static Response DefaultIfNull(Response? response, [CallerMemberName] string callerName = default!)
{
// 0x26F19335 is a magic number that hashed from "Snap.Hutao"
return response ?? new(0x26F19335, $"[{callerName}] 中的请求异常");
}
/// <summary>
/// 返回本体或带有消息提示的默认值
/// </summary>