Merge branch 'DGP-Studio:main' into main

This commit is contained in:
Xhichn
2023-03-24 20:24:37 +08:00
committed by GitHub
25 changed files with 396 additions and 79 deletions

View File

@@ -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

13
CODE_OF_CONDUCT.md Normal file
View File

@@ -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.

View File

@@ -1,25 +1,31 @@
![](https://repository-images.githubusercontent.com/482734649/5f8cf574-2ef0-43e9-aa8d-6cf094b54dd9)
![](https://repository-images.githubusercontent.com/482734649/c47a0a8e-868d-4d07-a66f-6d4473abfe46)
胡桃工具箱是一个 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
## 下载使用
[<img src="https://get.microsoft.com/images/zh-cn%20light.svg" width="30%" height="30%">](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
![Snap.Hutao](https://repobeats.axiom.co/api/embed/f029553fbe0c60689b1710476ec8512452163fc9.svg)

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

@@ -32,7 +32,7 @@ internal sealed class ExceptionRecorder
{
#if RELEASE
#pragma warning disable VSTHRD002
Ioc.Default.GetRequiredService<Web.Hutao.HomaClient2>().UploadLogAsync(e.Exception).GetAwaiter().GetResult();
Ioc.Default.GetRequiredService<Web.Hutao.HomaLogUploadClient>().UploadLogAsync(e.Exception).GetAwaiter().GetResult();
#pragma warning restore VSTHRD002
#endif
StringBuilder dataDetailBuilder = new();

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

@@ -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;
}

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

@@ -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<byte> szModuleLocal = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)pszModule);
if (entry.th32ProcessID == processId && szModuleLocal.SequenceEqual(moduleName))
{
return entry;
}

View File

@@ -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
/// <param name="memoryCache">内存缓存</param>
/// <param name="appDbContext">数据库上下文</param>
/// <param name="options">Json序列化选项</param>
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<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

@@ -244,7 +244,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.6.4-alpha" />
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.188-beta">
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.206-beta">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

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

@@ -50,7 +50,7 @@ internal sealed class AchievementImporter
{
if (await GetUIAFFromClipboardAsync().ConfigureAwait(false) is UIAF uiaf)
{
return await ImportAsync(achievementService.CurrentArchive!, uiaf).ConfigureAwait(false);
return await TryImportAsync(achievementService.CurrentArchive!, uiaf).ConfigureAwait(false);
}
else
{
@@ -85,7 +85,7 @@ internal sealed class AchievementImporter
if (isOk)
{
return await ImportAsync(achievementService.CurrentArchive, uiaf!).ConfigureAwait(false);
return await TryImportAsync(achievementService.CurrentArchive, uiaf!).ConfigureAwait(false);
}
else
{
@@ -114,7 +114,7 @@ internal sealed class AchievementImporter
}
}
private async Task<bool> ImportAsync(EntityAchievementArchive archive, UIAF uiaf)
private async Task<bool> TryImportAsync(EntityAchievementArchive archive, UIAF uiaf)
{
if (uiaf.IsCurrentVersionSupported())
{

View File

@@ -176,7 +176,7 @@ internal sealed class SpiralAbyssRecordViewModel : Abstraction.ViewModel, IRecip
private async Task UploadSpiralAbyssRecordAsync()
{
HomaClient homaClient = serviceProvider.GetRequiredService<HomaClient>();
HomaSpiralAbyssClient homaClient = serviceProvider.GetRequiredService<HomaSpiralAbyssClient>();
IInfoBarService infoBarService = serviceProvider.GetRequiredService<IInfoBarService>();
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))

View File

@@ -13,7 +13,7 @@ namespace Snap.Hutao.Web.Hutao;
/// </summary>
[HighQuality]
[HttpClient(HttpClientConfigration.Default)]
internal sealed class HomaClient2
internal sealed class HomaLogUploadClient
{
private readonly HttpClient httpClient;
@@ -21,7 +21,7 @@ internal sealed class HomaClient2
/// 构造一个新的胡桃日志客户端
/// </summary>
/// <param name="httpClient">Http客户端</param>
public HomaClient2(HttpClient httpClient)
public HomaLogUploadClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
@@ -50,4 +50,4 @@ internal sealed class HomaClient2
Info = exception.ToString(),
};
}
}
}

View File

@@ -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;
/// <summary>
/// 胡桃通行证客户端
/// </summary>
[HighQuality]
[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>
/// 构造一个新的胡桃通行证客户端
/// </summary>
/// <param name="httpClient">Http客户端</param>
public HomaPassportClient(HttpClient httpClient)
{
this.httpClient = httpClient;
}
/// <summary>
/// 异步获取验证码
/// </summary>
/// <param name="email">邮箱</param>
/// <param name="isResetPassword">是否重置账号密码</param>
/// <param name="token">取消令牌</param>
/// <returns>响应</returns>
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

@@ -19,12 +19,12 @@ namespace Snap.Hutao.Web.Hutao;
/// </summary>
[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<HomaClient> logger;
private readonly ILogger<HomaSpiralAbyssClient> logger;
/// <summary>
/// 构造一个新的胡桃API客户端
@@ -33,7 +33,7 @@ internal sealed class HomaClient
/// <param name="gameRecordClient">游戏记录客户端</param>
/// <param name="options">json序列化选项</param>
/// <param name="logger">日志器</param>
public HomaClient(HttpClient httpClient, GameRecordClient gameRecordClient, JsonSerializerOptions options, ILogger<HomaClient> logger)
public HomaSpiralAbyssClient(HttpClient httpClient, GameRecordClient gameRecordClient, JsonSerializerOptions options, ILogger<HomaSpiralAbyssClient> logger)
{
this.httpClient = httpClient;
this.gameRecordClient = gameRecordClient;

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>

View File

@@ -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;
/// <summary>
/// 内存拓展 for <see cref="Memory{T}"/> and <see cref="Span{T}"/>
/// </summary>
[HighQuality]
internal static class MemoryExtension
{
/// <summary>
/// 将 __CHAR_256 转换到 字符串
/// </summary>
/// <param name="char256">目标字符数组</param>
/// <returns>结果字符串</returns>
public static unsafe ReadOnlySpan<byte> AsNullTerminatedReadOnlySpan(this in __CHAR_256 char256)
{
fixed (CHAR* pszChar = &char256._0)
{
return MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)pszChar);
}
}
}