mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
update banner
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||

|
||||

|
||||
|
||||
## 下载使用
|
||||
[<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
BIN
res/HutaoRepoBanner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 699 KiB |
BIN
res/HutaoRepoBanner.psd
Normal file
BIN
res/HutaoRepoBanner.psd
Normal file
Binary file not shown.
@@ -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>
|
||||
/// 静态资源合约
|
||||
/// 新增合约时 请注意
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
77
src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
Normal file
77
src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
11
src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserService.cs
Normal file
11
src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserService.cs
Normal 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
|
||||
{
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -72,6 +72,10 @@
|
||||
Description="{Binding AppVersion}"
|
||||
Header="{shcm:ResourceString Name=AppName}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"/>
|
||||
<clw:SettingsCard
|
||||
Description="{Binding AppVersion}"
|
||||
Header="{shcm:ResourceString Name=ViewPageSettingHutaoPassportHeader}"
|
||||
HeaderIcon="{shcm:FontIcon Glyph=}"/>
|
||||
<clw:SettingsCard
|
||||
ActionIcon="{shcm:FontIcon Glyph=}"
|
||||
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=}">
|
||||
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user