diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index eabc2c3f..c577fa8c 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -15,20 +15,15 @@ body:
description: |-
请确保你已完整执行检查清单,否则你的 Issue 可能会被忽略
options:
- - label: 我已完整阅读[胡桃工具箱文档](https://hut.ao/advanced/FAQ.html),并认为我的问题没有在文档中得到解答
- required: true
+ - label: 我并未完整阅读[胡桃工具箱文档](https://hut.ao/advanced/FAQ.html)
- - label: 我知道文档站的导航栏中有**搜索功能**,且已经搜索过相关关键词
- required: true
+ - label: 我不知道文档站的导航栏中有**搜索功能**,也没有搜索过相关关键词
- - label: 我使用的操作系统是[受支持的版本](https://hut.ao/quick-start.html#%E6%9C%80%E4%BD%8E%E7%B3%BB%E7%BB%9F%E8%A6%81%E6%B1%82)
- required: true
+ - label: 我使用的操作系统是不[受支持的版本](https://hut.ao/quick-start.html#%E6%9C%80%E4%BD%8E%E7%B3%BB%E7%BB%9F%E8%A6%81%E6%B1%82)
- - label: 我**通过搜索功能**确认没有其他人已经提出了相同或类似的问题
- required: true
+ - label: 我没有**通过搜索功能**确认其他人提出过相同或类似的问题
- - label: 我明白上述的勾选项是**一个有助于快速排查问题的检查清单**,而不是随手确认的选项
- required: true
+ - label: 我不明白上述的勾选项是**一个有助于快速排查问题的检查清单**,而是随手确认的选项
- type: input
id: winver
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..decf6ee8
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,13 @@
+# Code of Conduct
+
+> Snap Hutao is adapting the following rules to keep the community safety.
+
+When participating in our open source community, we want all members to respect and support each other. To ensure the comfort and safety of our community members, we have established the following code of conduct:
+
+1. Respect diversity and inclusivity. We welcome people from different countries, regions, genders, sexual orientations, abilities, religions, and cultural backgrounds to participate in our community, and we encourage respect for all differences.
+2. Prohibit discrimination and harassment. We do not tolerate any form of discrimination, harassment, personal attacks, or insults. This includes but is not limited to race, gender, sexual orientation, age, religion, nationality, cultural background, physical and mental health status.
+3. Respect privacy and personal information. We protect the privacy and personal information of community members and prohibit the public disclosure of any private information. If you need to disclose certain information, please make sure you have obtained the relevant person's permission.
+4. Keep honesty and transparency. We expect community members to maintain honesty and transparency and not intentionally mislead or deceive others.
+5. Respect community rules and other members. We encourage community members to follow community rules and guidelines and maintain a polite and respectful attitude towards other members. If you find that other members are violating community rules, please report it to community administrators or organizers in a timely manner.
+
+The above is our community's code of conduct, and we expect all community members to abide by these rules. We will actively address behaviors that violate these rules. We believe that through mutual respect and support, we can build a friendly, inclusive, and beneficial open source community.
\ No newline at end of file
diff --git a/README.md b/README.md
index a70722f3..6b513eaa 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,31 @@
-
+
+
+胡桃工具箱是一个 Windows 平台的开源的原神工具箱,旨在帮助玩家获得更好的游戏体验; 它是对官方移动端工具的一种非破坏性功能扩展,为不习惯在移动端进行原神游戏的 PC 玩家提供一个在 Windows 平台下获得接近移动端功能权利的途径
+
+Snap Hutao is an open-source Genshin Impact toolbox on Windows platform, aim to provide a better gaming experience for players. It's an nondestructive feature extension from Genshin Impact's official mobile application, to provide similar feature on desktop, to allow PC gamers gain deserved benefits from mobile platforms.
+
+## 下载使用 / Download
-## 下载使用
[
](https://apps.microsoft.com/store/detail/snap-hutao/9PH4NXJ2JN52)
-## 贡献
+## 贡献 / Contribute
-* [向我们提交 PR](https://github.com/DGP-Studio/Snap.Hutao/pulls)
-* [在 Crowdin 上进行本地化](https://translate.hut.ao/)
+* [向我们提交 PR / Make Pull Requests](https://github.com/DGP-Studio/Snap.Hutao/pulls)
+* [在 Crowdin 上进行本地化 / Translate project on Crowdin](https://translate.hut.ao/)
+* [为我们更新文档 / Enhance our Document ](https://github.com/DGP-Studio/Snap.Hutao.Docs)
-## 特别感谢
+## 特别感谢 / Special Thanks
* [HolographicHat](https://github.com/HolographicHat)
* [UIGF organization](https://uigf.org)
-### 特定的原神项目
+### 特定的原神项目 / Specific Genshin-related Project
* [biuuu/genshin-wish-export](https://github.com/biuuu/genshin-wish-export)
* [xunkong/xunkong](https://github.com/xunkong/xunkong)
* [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
-### 使用的技术栈
+### 使用的技术栈 / Tech Stack
* [CommunityToolkit/dotnet](https://github.com/CommunityToolkit/dotnet)
* [CommunityToolkit/Labs-Windows](https://github.com/CommunityToolkit/Labs-Windows)
@@ -34,11 +40,11 @@
* [microsoft/WindowsAppSDK](https://github.com/microsoft/WindowsAppSDK)
* [microsoft/microsoft-ui-xaml](https://github.com/microsoft/microsoft-ui-xaml)
-### 支撑项目
+### 支撑项目 / Supporter Project
* [Snap.Hutao.Server](https://github.com/DGP-Studio/Snap.Hutao.Server)
* [Snap.Metadata](https://github.com/DGP-Studio/Snap.Metadata)
* [Snap.Data.Mapper](https://github.com/DGP-Studio/Snap.Data.Mapper)
-## 近期活跃数据
+## 近期活跃数据 / Active Stat

diff --git a/res/HutaoRepoBanner.png b/res/HutaoRepoBanner.png
new file mode 100644
index 00000000..58dffbe9
Binary files /dev/null and b/res/HutaoRepoBanner.png differ
diff --git a/res/HutaoRepoBanner.psd b/res/HutaoRepoBanner.psd
new file mode 100644
index 00000000..6705e4e7
Binary files /dev/null and b/res/HutaoRepoBanner.psd differ
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ExceptionRecorder.cs b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ExceptionRecorder.cs
index 06b90bc2..a41c515f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ExceptionRecorder.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/ExceptionService/ExceptionRecorder.cs
@@ -32,7 +32,7 @@ internal sealed class ExceptionRecorder
{
#if RELEASE
#pragma warning disable VSTHRD002
- Ioc.Default.GetRequiredService().UploadLogAsync(e.Exception).GetAwaiter().GetResult();
+ Ioc.Default.GetRequiredService().UploadLogAsync(e.Exception).GetAwaiter().GetResult();
#pragma warning restore VSTHRD002
#endif
StringBuilder dataDetailBuilder = new();
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs b/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
index b55885c1..16ed0ca9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Setting/SettingKeys.cs
@@ -29,6 +29,16 @@ internal static class SettingKeys
///
public const string DataFolderPath = "DataFolderPath";
+ ///
+ /// 通行证用户名(邮箱)
+ ///
+ public const string PassportUserName = "PassportUserName";
+
+ ///
+ /// 通行证密码
+ ///
+ public const string PassportPassword = "PassportPassword";
+
///
/// 静态资源合约
/// 新增合约时 请注意
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs
index f3fca7fb..d420b50e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs
@@ -214,7 +214,7 @@ internal sealed class User : ObservableObject
return false;
}
- if (await TrySetCookieTokenAsync(scope.ServiceProvider, token).ConfigureAwait(false))
+ if (!await TrySetCookieTokenAsync(scope.ServiceProvider, token).ConfigureAwait(false))
{
return false;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
index 5ab6c40b..3fba8d01 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs
@@ -4281,6 +4281,15 @@ namespace Snap.Hutao.Resource.Localization {
}
}
+ ///
+ /// 查找类似 胡桃账号 的本地化字符串。
+ ///
+ internal static string ViewPageSettingHutaoPassportHeader {
+ get {
+ return ResourceManager.GetString("ViewPageSettingHutaoPassportHeader", resourceCulture);
+ }
+ }
+
///
/// 查找类似 在完整阅读原神和胡桃工具箱用户协议后,我选择启用「启动游戏-高级功能」 的本地化字符串。
///
@@ -4488,6 +4497,24 @@ namespace Snap.Hutao.Resource.Localization {
}
}
+ ///
+ /// 查找类似 登录失败,请重新登录 的本地化字符串。
+ ///
+ internal static string ViewServiceHutaoUserLoginFailHint {
+ get {
+ return ResourceManager.GetString("ViewServiceHutaoUserLoginFailHint", resourceCulture);
+ }
+ }
+
+ ///
+ /// 查找类似 立即登录或注册 的本地化字符串。
+ ///
+ internal static string ViewServiceHutaoUserLoginOrRegisterHint {
+ get {
+ return ResourceManager.GetString("ViewServiceHutaoUserLoginOrRegisterHint", resourceCulture);
+ }
+ }
+
///
/// 查找类似 战斗数据 的本地化字符串。
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
index 7358557b..82831ddc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
+++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx
@@ -1797,4 +1797,13 @@
是否永久删除用户数据
+
+ 胡桃账号
+
+
+ 登录失败,请重新登录
+
+
+ 立即登录或注册
+
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs
index 6172a5cf..d5911f91 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/Unlocker/GameFpsUnlocker.cs
@@ -81,10 +81,11 @@ internal sealed class GameFpsUnlocker : IGameFpsUnlocker
try
{
Marshal.ThrowExceptionForHR(Marshal.GetLastPInvokeError());
-
foreach (MODULEENTRY32 entry in StructMarshal.EnumerateModuleEntry32(snapshot))
{
- if (entry.th32ProcessID == processId && entry.szModule.AsNullTerminatedReadOnlySpan().SequenceEqual(moduleName))
+ __CHAR_256* pszModule = &entry.szModule;
+ ReadOnlySpan szModuleLocal = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)pszModule);
+ if (entry.th32ProcessID == processId && szModuleLocal.SequenceEqual(moduleName))
{
return entry;
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs
index c8e48509..0360bc72 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoService.cs
@@ -18,7 +18,7 @@ namespace Snap.Hutao.Service.Hutao;
[Injection(InjectAs.Scoped, typeof(IHutaoService))]
internal sealed class HutaoService : IHutaoService
{
- private readonly HomaClient homaClient;
+ private readonly HomaSpiralAbyssClient homaClient;
private readonly IMemoryCache memoryCache;
private readonly AppDbContext appDbContext;
private readonly JsonSerializerOptions options;
@@ -30,7 +30,7 @@ internal sealed class HutaoService : IHutaoService
/// 内存缓存
/// 数据库上下文
/// Json序列化选项
- public HutaoService(HomaClient homaClient, IMemoryCache memoryCache, AppDbContext appDbContext, JsonSerializerOptions options)
+ public HutaoService(HomaSpiralAbyssClient homaClient, IMemoryCache memoryCache, AppDbContext appDbContext, JsonSerializerOptions options)
{
this.homaClient = homaClient;
this.memoryCache = memoryCache;
@@ -103,18 +103,21 @@ internal sealed class HutaoService : IHutaoService
}
Response 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));
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
new file mode 100644
index 00000000..8de4fd32
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/HutaoUserService.cs
@@ -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;
+
+///
+/// 胡桃用户服务
+///
+[Injection(InjectAs.Singleton, typeof(IHutaoUserService))]
+internal sealed class HutaoUserService : IHutaoUserService, IHutaoUserServiceInitialization
+{
+ private readonly HomaPassportClient passportClient;
+ private readonly TaskCompletionSource initializeCompletionSource = new();
+
+ private bool isInitialized;
+
+ ///
+ /// 构造一个新的胡桃用户服务
+ ///
+ /// 通行证客户端
+ public HutaoUserService(HomaPassportClient passportClient)
+ {
+ this.passportClient = passportClient;
+ }
+
+ ///
+ /// 用户名
+ ///
+ public string? UserName { get; private set; }
+
+ ///
+ /// 访问令牌
+ ///
+ public string? Token { get; private set; }
+
+ ///
+ /// 异步初始化
+ ///
+ /// 任务
+ public async ValueTask InitializeAsync()
+ {
+ await initializeCompletionSource.Task.ConfigureAwait(false);
+ return isInitialized;
+ }
+
+ ///
+ 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 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();
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserService.cs
new file mode 100644
index 00000000..87c42535
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserService.cs
@@ -0,0 +1,11 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.Hutao;
+
+///
+/// 胡桃用户服务
+///
+internal interface IHutaoUserService : ICastableService
+{
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs
new file mode 100644
index 00000000..e5a46442
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Hutao/IHutaoUserServiceInitialization.cs
@@ -0,0 +1,17 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Service.Hutao;
+
+///
+/// 指示该类为用户服务初始化器
+///
+internal interface IHutaoUserServiceInitialization
+{
+ ///
+ /// 异步初始化
+ ///
+ /// 取消令牌
+ /// 任务
+ Task InitializeInternalAsync(CancellationToken token = default);
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 05d81176..fa02c6b9 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -244,7 +244,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
index 650221b5..5c082d14 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/SettingPage.xaml
@@ -72,6 +72,10 @@
Description="{Binding AppVersion}"
Header="{shcm:ResourceString Name=AppName}"
HeaderIcon="{shcm:FontIcon Glyph=}"/>
+
-
@@ -217,6 +215,7 @@
IsOn="{Binding Options.IsAdvancedLaunchOptionsEnabled, Mode=TwoWay}"/>
ImportAsync(EntityAchievementArchive archive, UIAF uiaf)
+ private async Task TryImportAsync(EntityAchievementArchive archive, UIAF uiaf)
{
if (uiaf.IsCurrentVersionSupported())
{
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs
index fafda204..0bcbaaca 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SpiralAbyss/SpiralAbyssRecordViewModel.cs
@@ -176,7 +176,7 @@ internal sealed class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecip
private async Task UploadSpiralAbyssRecordAsync()
{
- HomaClient homaClient = serviceProvider.GetRequiredService();
+ HomaSpiralAbyssClient homaClient = serviceProvider.GetRequiredService();
IInfoBarService infoBarService = serviceProvider.GetRequiredService();
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaLogUploadClient.cs
similarity index 93%
rename from src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
rename to src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaLogUploadClient.cs
index eea02306..d7d91cec 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient2.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaLogUploadClient.cs
@@ -13,7 +13,7 @@ namespace Snap.Hutao.Web.Hutao;
///
[HighQuality]
[HttpClient(HttpClientConfigration.Default)]
-internal sealed class HomaClient2
+internal sealed class HomaLogUploadClient
{
private readonly HttpClient httpClient;
@@ -21,7 +21,7 @@ internal sealed class HomaClient2
/// 构造一个新的胡桃日志客户端
///
/// Http客户端
- public HomaClient2(HttpClient httpClient)
+ public HomaLogUploadClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
@@ -50,4 +50,4 @@ internal sealed class HomaClient2
Info = exception.ToString(),
};
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaPassportClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaPassportClient.cs
new file mode 100644
index 00000000..9b6e3c0a
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaPassportClient.cs
@@ -0,0 +1,147 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
+using Snap.Hutao.Web.Response;
+using System.Net.Http;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Snap.Hutao.Web.Hutao;
+
+///
+/// 胡桃通行证客户端
+///
+[HighQuality]
+[HttpClient(HttpClientConfigration.Default)]
+internal sealed class HomaPassportClient
+{
+ ///
+ /// 通行证请求公钥
+ ///
+ 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;
+
+ ///
+ /// 构造一个新的胡桃通行证客户端
+ ///
+ /// Http客户端
+ public HomaPassportClient(HttpClient httpClient)
+ {
+ this.httpClient = httpClient;
+ }
+
+ ///
+ /// 异步获取验证码
+ ///
+ /// 邮箱
+ /// 是否重置账号密码
+ /// 取消令牌
+ /// 响应
+ public async Task VerifyAsync(string email, bool isResetPassword, CancellationToken token = default)
+ {
+ Dictionary data = new()
+ {
+ ["UserName"] = Encrypt(email),
+ ["IsResetPassword"] = isResetPassword,
+ };
+
+ Response.Response? resp = await httpClient
+ .TryCatchPostAsJsonAsync, Response.Response>(HutaoEndpoints.PassportVerify, data, token)
+ .ConfigureAwait(false);
+
+ return Response.Response.DefaultIfNull(resp);
+ }
+
+ ///
+ /// 异步注册
+ ///
+ /// 邮箱
+ /// 密码
+ /// 验证码
+ /// 取消令牌
+ /// 响应,包含登录令牌
+ public async Task> RegisterAsync(string email, string password, string verifyCode, CancellationToken token = default)
+ {
+ Dictionary data = new()
+ {
+ ["UserName"] = Encrypt(email),
+ ["Password"] = Encrypt(password),
+ ["VerifyCode"] = Encrypt(verifyCode),
+ };
+
+ Response? resp = await httpClient
+ .TryCatchPostAsJsonAsync, Response>(HutaoEndpoints.PassportRegister, data, token)
+ .ConfigureAwait(false);
+
+ return Response.Response.DefaultIfNull(resp);
+ }
+
+ ///
+ /// 异步重置密码
+ ///
+ /// 邮箱
+ /// 密码
+ /// 验证码
+ /// 取消令牌
+ /// 响应,包含登录令牌
+ public async Task> ResetPasswordAsync(string email, string password, string verifyCode, CancellationToken token = default)
+ {
+ Dictionary data = new()
+ {
+ ["UserName"] = Encrypt(email),
+ ["Password"] = Encrypt(password),
+ ["VerifyCode"] = Encrypt(verifyCode),
+ };
+
+ Response? resp = await httpClient
+ .TryCatchPostAsJsonAsync, Response>(HutaoEndpoints.PassportResetPassword, data, token)
+ .ConfigureAwait(false);
+
+ return Response.Response.DefaultIfNull(resp);
+ }
+
+ ///
+ /// 异步登录
+ ///
+ /// 邮箱
+ /// 密码
+ /// 取消令牌
+ /// 响应,包含登录令牌
+ public async Task> LoginAsync(string email, string password, CancellationToken token = default)
+ {
+ Dictionary data = new()
+ {
+ ["UserName"] = Encrypt(email),
+ ["Password"] = Encrypt(password),
+ };
+
+ Response? resp = await httpClient
+ .TryCatchPostAsJsonAsync, Response>(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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaSpiralAbyssClient.cs
similarity index 97%
rename from src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
rename to src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaSpiralAbyssClient.cs
index 5a71de82..440e2596 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaSpiralAbyssClient.cs
@@ -19,12 +19,12 @@ namespace Snap.Hutao.Web.Hutao;
///
[HighQuality]
[HttpClient(HttpClientConfigration.Default)]
-internal sealed class HomaClient
+internal sealed class HomaSpiralAbyssClient
{
private readonly HttpClient httpClient;
private readonly GameRecordClient gameRecordClient;
private readonly JsonSerializerOptions options;
- private readonly ILogger logger;
+ private readonly ILogger logger;
///
/// 构造一个新的胡桃API客户端
@@ -33,7 +33,7 @@ internal sealed class HomaClient
/// 游戏记录客户端
/// json序列化选项
/// 日志器
- public HomaClient(HttpClient httpClient, GameRecordClient gameRecordClient, JsonSerializerOptions options, ILogger logger)
+ public HomaSpiralAbyssClient(HttpClient httpClient, GameRecordClient gameRecordClient, JsonSerializerOptions options, ILogger logger)
{
this.httpClient = httpClient;
this.gameRecordClient = gameRecordClient;
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs
index 91170743..39244bd8 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/HutaoEndpoints.cs
@@ -16,6 +16,29 @@ internal static class HutaoEndpoints
///
public const string StaticHutao = "static.hut.ao";
+ #region Passport
+
+ ///
+ /// 获取注册验证码
+ ///
+ public const string PassportVerify = $"{HomaSnapGenshinApi}/Passport/Verify";
+
+ ///
+ /// 注册账号
+ ///
+ public const string PassportRegister = $"{HomaSnapGenshinApi}/Passport/Register";
+
+ ///
+ /// 重设密码
+ ///
+ public const string PassportResetPassword = $"{HomaSnapGenshinApi}/Passport/ResetPassword";
+
+ ///
+ /// 登录
+ ///
+ public const string PassportLogin = $"{HomaSnapGenshinApi}/Passport/Login";
+ #endregion
+
#region HutaoAPI
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs b/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs
index e7d71a9a..9a89be1e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Response/Response.cs
@@ -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}] 中的请求异常");
+ }
+
///
/// 返回本体或带有消息提示的默认值
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Win32/MemoryExtension.cs b/src/Snap.Hutao/Snap.Hutao/Win32/MemoryExtension.cs
deleted file mode 100644
index 95483176..00000000
--- a/src/Snap.Hutao/Snap.Hutao/Win32/MemoryExtension.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) DGP Studio. All rights reserved.
-// Licensed under the MIT license.
-
-using System.Runtime.InteropServices;
-using Windows.Win32.Foundation;
-
-namespace Snap.Hutao.Win32;
-
-///
-/// 内存拓展 for and
-///
-[HighQuality]
-internal static class MemoryExtension
-{
- ///
- /// 将 __CHAR_256 转换到 字符串
- ///
- /// 目标字符数组
- /// 结果字符串
- public static unsafe ReadOnlySpan AsNullTerminatedReadOnlySpan(this in __CHAR_256 char256)
- {
- fixed (CHAR* pszChar = &char256._0)
- {
- return MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)pszChar);
- }
- }
-}
\ No newline at end of file