Upload spiral abyss record for hoyolab user & clean

This commit is contained in:
Xhichn
2023-03-24 21:44:05 +08:00
parent 9decb67cff
commit 523374ed3d
9 changed files with 94 additions and 49 deletions

View File

@@ -93,7 +93,7 @@ internal sealed class AvatarInfoDbOperation
Response<RecordPlayerInfo> playerInfoResponse;
Response<Web.Hoyolab.Takumi.GameRecord.Avatar.CharacterWrapper> charactersResponse;
if (userAndUid.Uid.Region == "cn_gf01" || userAndUid.Uid.Region == "cn_qd01")
if (!userAndUid.User.IsOversea)
{
GameRecordClient gameRecordClient = Ioc.Default.GetRequiredService<GameRecordClient>();
playerInfoResponse = await gameRecordClient

View File

@@ -38,7 +38,7 @@ internal sealed class GachaLogQueryStokenProvider : IGachaLogQueryProvider
{
if (UserAndUid.TryFromUser(userService.Current, out UserAndUid? userAndUid))
{
if (userAndUid.Uid.Region != "cn_gf01" && userAndUid.Uid.Region != "cn_qd01")
if (userAndUid.User.IsOversea)
{
return new(false, "Unsupported for hoyoverse account");
}

View File

@@ -75,15 +75,15 @@ internal class SpiralAbyssRecordService : ISpiralAbyssRecordService
Response<Web.Hoyolab.Takumi.GameRecord.SpiralAbyss.SpiralAbyss> response;
// server determination
if (userAndUid.Uid.Region == "cn_gf01" || userAndUid.Uid.Region == "cn_qd01")
if (userAndUid.User.IsOversea)
{
response = await gameRecordClient
response = await gameRecordClientOs
.GetSpiralAbyssAsync(userAndUid, schedule)
.ConfigureAwait(false);
}
else
{
response = await gameRecordClientOs
response = await gameRecordClient
.GetSpiralAbyssAsync(userAndUid, schedule)
.ConfigureAwait(false);
}

View File

@@ -27,13 +27,15 @@
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox
<TextBox
TextChanging="TextBoxOnTextChanging"
InputScope="Number"
MaxLength="9"
Grid.Column="0"
Width="240"
MaxLength="9"
x:Name="UidInput"
Margin="0,0,16,0"
PlaceholderText="<EFBFBD><EFBFBD><EFBFBD><EFBFBD>дͨ<EFBFBD><EFBFBD>֤ID"
PlaceholderText="Please fill in your User ID here"
HorizontalAlignment="Right"/>
<Button
Grid.Column="1"

View File

@@ -2,27 +2,25 @@
// Licensed under the MIT license.
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.User;
using Snap.Hutao.View.Dialog;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Passport;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using Snap.Hutao.Web.Response;
using System.Diagnostics.Eventing.Reader;
namespace Snap.Hutao.View.Page;
/// <summary>
/// <EFBFBD><EFBFBD>¼<EFBFBD>׹<EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD>֤ҳ<EFBFBD><EFBFBD>
/// 登录米哈游通行证页面
/// </summary>
[HighQuality]
internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Controls.Page
{
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>µĵ<EFBFBD>¼<EFBFBD>׹<EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><EFBFBD>֤ҳ<EFBFBD><EFBFBD>
/// 构造一个新的登录米哈游通行证页面
/// </summary>
public LoginHoyoverseUserPage()
{
@@ -65,14 +63,14 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
if (uid.Length != 9)
{
await ThreadHelper.SwitchToMainThreadAsync();
infoBarService.Warning($"<22><><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>д<EFBFBD><D0B4><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8>֤ ID!");
infoBarService.Information($"请在页面右上方的输入框处填写你的通行证 ID!");
return;
}
Cookie loginTicketCookie = Cookie.FromCoreWebView2Cookies(cookies);
loginTicketCookie["login_uid"] = uid;
// ʹ<EFBFBD><EFBFBD> loginTicket <EFBFBD><EFBFBD>ȡ stoken
// 使用 loginTicket 获取 stoken
Response<ListWrapper<NameToken>> multiTokenResponse = await Ioc.Default
.GetRequiredService<AuthClientOs>()
.GetMultiTokenByLoginTicketAsync(loginTicketCookie, token)
@@ -86,7 +84,7 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
Dictionary<string, string> multiTokenMap = multiTokenResponse.Data.List.ToDictionary(n => n.Name, n => n.Token);
Cookie hoyoLabCookie = Cookie.Parse($"stoken={multiTokenMap["stoken"]}; stuid={uid}");
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> cookie <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>û<EFBFBD>
// 处理 cookie 并添加用户
(UserOptionResult result, string nickname) = await Ioc.Default
.GetRequiredService<IUserService>()
.ProcessInputOsCookieAsync(hoyoLabCookie)
@@ -104,16 +102,16 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
vm.SelectedUser = vm.Users.Single();
}
infoBarService.Success($"<EFBFBD>û<EFBFBD> [{nickname}] <EFBFBD><EFBFBD><EFBFBD>ӳɹ<EFBFBD>");
infoBarService.Success($"用户 [{nickname}] 添加成功");
break;
case UserOptionResult.Incomplete:
infoBarService.Information($"<EFBFBD><EFBFBD> Cookie <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>");
infoBarService.Information($" Cookie 不完整,操作失败");
break;
case UserOptionResult.Invalid:
infoBarService.Information($"<EFBFBD><EFBFBD> Cookie <EFBFBD><EFBFBD>Ч<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʧ<EFBFBD><EFBFBD>");
infoBarService.Information($" Cookie 无效,操作失败");
break;
case UserOptionResult.Updated:
infoBarService.Success($"<EFBFBD>û<EFBFBD> [{nickname}] <EFBFBD><EFBFBD><EFBFBD>³ɹ<EFBFBD>");
infoBarService.Success($"用户 [{nickname}] 更新成功");
break;
default:
throw Must.NeverHappen();
@@ -124,4 +122,9 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
{
HandleCurrentCookieAsync(CancellationToken.None).SafeForget();
}
private void TextBoxOnTextChanging(TextBox sender, TextBoxTextChangingEventArgs args)
{
sender.Text = new(sender.Text.Where(char.IsDigit).ToArray());
}
}

View File

@@ -118,8 +118,9 @@ internal sealed partial class Cookie
/// <summary>
/// 提取其中的 stoken 信息
/// Used for hoyolab account.
/// </summary>
/// <param name="cookie">含有 Stoken 的 cookie</param>
/// <param name="cookie">A cookie contains stoken and stuid, without mid.</param>
/// <returns>是否获取成功</returns>
public bool TryGetAsStokenV1([NotNullWhen(true)] out Cookie? cookie)
{

View File

@@ -44,14 +44,16 @@ internal sealed class PassportClientOs
public async Task<Response<UidCookieToken>> GetCookieAccountInfoBySTokenAsync(User user, CancellationToken token = default)
{
Response<UidCookieToken>? resp = null;
string? stoken = user.SToken["stoken"];
if (stoken == null || user.Aid == null)
if (user.SToken == null)
{
return Response.Response.DefaultIfNull(resp);
}
StokenData data = new(stoken, user.Aid);
string stoken = user.SToken.GetValueOrDefault(Cookie.STOKEN)!;
// Post json with stoken and uid (stuid/ltuid)
StokenData data = new(stoken, user.Aid!);
resp = await httpClient
.SetUser(user, CookieType.SToken)
.TryCatchPostAsJsonAsync<StokenData, Response<UidCookieToken>>(ApiOsEndpoints.AccountGetCookieTokenBySToken, data, options, logger, token)
@@ -70,14 +72,16 @@ internal sealed class PassportClientOs
public async Task<Response<LtokenWrapper>> GetLtokenBySTokenAsync(User user, CancellationToken token)
{
Response<LtokenWrapper>? resp = null;
string? stoken = user.SToken["stoken"];
if (stoken == null || user.Aid == null)
if (user.SToken == null)
{
return Response.Response.DefaultIfNull(resp);
}
StokenData data = new(stoken, user.Aid);
string stoken = user.SToken.GetValueOrDefault(Cookie.STOKEN)!;
// Post json with stoken and uid (stuid/ltuid)
StokenData data = new(stoken, user.Aid!);
resp = await httpClient
.SetUser(user, CookieType.SToken)
.TryCatchPostAsJsonAsync<StokenData, Response<LtokenWrapper>>(ApiOsEndpoints.AccountGetLtokenByStoken, data, options, logger, token)

View File

@@ -80,18 +80,20 @@ internal sealed class CalculateClient
Response<ListWrapper<Avatar>>? resp;
// 根据 uid 所属服务器选择 referer 与 api
string referer = ApiOsEndpoints.ActHoyolabReferer;
string endpoint = ApiOsEndpoints.CalculateOsSyncAvatarList;
string referer;
string endpoint;
if (userAndUid.Uid.Region == "cn_gf01" || userAndUid.Uid.Region == "cn_qd01")
if (userAndUid.User.IsOversea)
{
referer = ApiEndpoints.WebStaticMihoyoReferer;
endpoint = ApiEndpoints.CalculateSyncAvatarList;
referer = ApiOsEndpoints.ActHoyolabReferer;
endpoint = ApiOsEndpoints.CalculateOsSyncAvatarList;
httpClient.SetUser(userAndUid.User, CookieType.CookieToken);
}
else
{
httpClient.SetUser(userAndUid.User, CookieType.Cookie);
referer = ApiEndpoints.WebStaticMihoyoReferer;
endpoint = ApiEndpoints.CalculateSyncAvatarList;
httpClient.SetUser(userAndUid.User, CookieType.CookieToken);
}
do
@@ -129,7 +131,7 @@ internal sealed class CalculateClient
public async Task<Response<AvatarDetail>> GetAvatarDetailAsync(UserAndUid userAndUid, Avatar avatar, CancellationToken token = default)
{
Response<AvatarDetail>? resp;
if (userAndUid.Uid.Region == "cn_gf01" || userAndUid.Uid.Region == "cn_qd01")
if (!userAndUid.User.IsOversea)
{
resp = await httpClient
.SetUser(userAndUid.User, CookieType.CookieToken)
@@ -139,10 +141,11 @@ internal sealed class CalculateClient
else
{
resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
.SetUser(userAndUid.User, CookieType.CookieToken)
.TryCatchGetFromJsonAsync<Response<AvatarDetail>>(ApiOsEndpoints.CalculateOsSyncAvatarDetail(avatar.Id, userAndUid.Uid.Value), options, logger, token)
.ConfigureAwait(false);
}
return Response.Response.DefaultIfNull(resp);
}

View File

@@ -23,6 +23,7 @@ internal sealed class HomaSpiralAbyssClient
{
private readonly HttpClient httpClient;
private readonly GameRecordClient gameRecordClient;
private readonly GameRecordClientOs gameRecordClientOs;
private readonly JsonSerializerOptions options;
private readonly ILogger<HomaSpiralAbyssClient> logger;
@@ -33,10 +34,11 @@ internal sealed class HomaSpiralAbyssClient
/// <param name="gameRecordClient">游戏记录客户端</param>
/// <param name="options">json序列化选项</param>
/// <param name="logger">日志器</param>
public HomaSpiralAbyssClient(HttpClient httpClient, GameRecordClient gameRecordClient, JsonSerializerOptions options, ILogger<HomaSpiralAbyssClient> logger)
public HomaSpiralAbyssClient(HttpClient httpClient, GameRecordClient gameRecordClient, GameRecordClientOs gameRecordClientOs, JsonSerializerOptions options, ILogger<HomaSpiralAbyssClient> logger)
{
this.httpClient = httpClient;
this.gameRecordClient = gameRecordClient;
this.gameRecordClientOs = gameRecordClientOs;
this.options = options;
this.logger = logger;
}
@@ -186,25 +188,55 @@ internal sealed class HomaSpiralAbyssClient
/// <returns>玩家记录</returns>
public async Task<SimpleRecord?> GetPlayerRecordAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<PlayerInfo> playerInfoResponse = await gameRecordClient
if (userAndUid.User.IsOversea)
{
// for oversea server
Response<PlayerInfo> playerInfoResponse = await gameRecordClientOs
.GetPlayerInfoAsync(userAndUid, token)
.ConfigureAwait(false);
if (playerInfoResponse.IsOk())
{
Response<CharacterWrapper> charactersResponse = await gameRecordClient
.GetCharactersAsync(userAndUid, playerInfoResponse.Data, token)
.ConfigureAwait(false);
if (charactersResponse.IsOk())
if (playerInfoResponse.IsOk())
{
Response<SpiralAbyss> spiralAbyssResponse = await gameRecordClient
.GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Current, token)
.ConfigureAwait(false);
Response<CharacterWrapper> charactersResponse = await gameRecordClientOs
.GetCharactersAsync(userAndUid, playerInfoResponse.Data, token)
.ConfigureAwait(false);
if (spiralAbyssResponse.IsOk())
if (charactersResponse.IsOk())
{
return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data);
Response<SpiralAbyss> spiralAbyssResponse = await gameRecordClientOs
.GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Current, token)
.ConfigureAwait(false);
if (spiralAbyssResponse.IsOk())
{
return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data);
}
}
}
}
else
{
// for cn server
Response<PlayerInfo> playerInfoResponse = await gameRecordClient
.GetPlayerInfoAsync(userAndUid, token)
.ConfigureAwait(false);
if (playerInfoResponse.IsOk())
{
Response<CharacterWrapper> charactersResponse = await gameRecordClient
.GetCharactersAsync(userAndUid, playerInfoResponse.Data, token)
.ConfigureAwait(false);
if (charactersResponse.IsOk())
{
Response<SpiralAbyss> spiralAbyssResponse = await gameRecordClient
.GetSpiralAbyssAsync(userAndUid, SpiralAbyssSchedule.Current, token)
.ConfigureAwait(false);
if (spiralAbyssResponse.IsOk())
{
return new(userAndUid.Uid.Value, charactersResponse.Data.Avatars, spiralAbyssResponse.Data);
}
}
}
}