fix HoYoLAB webview login

This commit is contained in:
DismissedLight
2023-03-30 13:07:15 +08:00
parent 04e9f7db72
commit 3dd7a8c85f
9 changed files with 147 additions and 116 deletions

View File

@@ -12,7 +12,7 @@
<Identity
Name="60568DGPStudio.SnapHutao"
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
Version="1.5.4.0" />
Version="1.6.0.0" />
<Properties>
<DisplayName>Snap Hutao</DisplayName>

View File

@@ -194,7 +194,7 @@ internal class UserService : IUserService
public async Task<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(Cookie cookie, bool isOversea)
{
await ThreadHelper.SwitchToBackgroundAsync();
string? mid = cookie.GetValueOrDefault(isOversea ? Cookie.MID : Cookie.STUID);
string? mid = cookie.GetValueOrDefault(isOversea ? Cookie.STUID : Cookie.MID);
if (mid == null)
{

View File

@@ -21,7 +21,12 @@
VerticalAlignment="Top"
PlaceholderText="{shcm:ResourceString Name=ViewDialogUserInputPlaceholder}"
TextChanged="InputTextChanged"/>
<StackPanel Margin="0,-48,0,0">
<StackPanel Margin="0,0,0,0">
<StackPanel.Resources>
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
<x:Double x:Key="SettingsCardWrapNoIconThreshold">0</x:Double>
<x:Double x:Key="SettingsCardMinHeight">0</x:Double>
</StackPanel.Resources>
<clw:SettingsCard
HorizontalAlignment="Stretch"
Description="{shcm:ResourceString Name=ViewDialogUserDocumentDescription}"

View File

@@ -18,33 +18,11 @@
Margin="12,0,0,0"
VerticalAlignment="Center"
Text="{shcm:ResourceString Name=ViewPageLoginMihoyoUserTitle}"/>
<Grid
Grid.Row="0"
<Button
Margin="16"
HorizontalAlignment="Right"
VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox
x:Name="UidInputText"
Grid.Column="0"
Width="240"
Margin="0,0,16,0"
HorizontalAlignment="Right"
InputScope="Number"
MaxLength="9"
PlaceholderText="{shcm:ResourceString Name=ViewPageLoginHoyoverseUserHint}"/>
<Button
Grid.Column="1"
Click="CookieButtonClick"
Content="{shcm:ResourceString Name=ViewPageLoginMihoyoUserLoggedInAction}"/>
</Grid>
<WebView2
x:Name="WebView"
Grid.Row="2"
Margin="0,0,0,0"/>
Click="CookieButtonClick"
Content="{shcm:ResourceString Name=ViewPageLoginMihoyoUserLoggedInAction}"/>
<WebView2 x:Name="WebView" Grid.Row="2"/>
</Grid>
</Page>

View File

@@ -6,9 +6,12 @@ using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service.Abstraction;
using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.User;
using Snap.Hutao.Web;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using Snap.Hutao.Web.Request;
using Snap.Hutao.Web.Response;
using System.Net.Http;
namespace Snap.Hutao.View.Page;
@@ -25,6 +28,26 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
InitializeComponent();
}
private static async Task<string> GetUidFromCookieAsync(IServiceProvider serviceProvider, Cookie cookie, CancellationToken token = default)
{
JsonSerializerOptions options = serviceProvider.GetRequiredService<JsonSerializerOptions>();
ILogger<LoginHoyoverseUserPage> logger = serviceProvider.GetRequiredService<ILogger<LoginHoyoverseUserPage>>();
HttpClient httpClient = serviceProvider.GetRequiredService<HttpClient>();
httpClient.DefaultRequestHeaders.Set("Cookie", cookie.ToString());
WebApiResponse<AccountInfoWrapper>? resp = await httpClient
.TryCatchGetFromJsonAsync<WebApiResponse<AccountInfoWrapper>>(ApiOsEndpoints.WebApiOsAccountLoginByCookie, options, logger, token)
.ConfigureAwait(false);
if (resp != null)
{
return resp.Data.AccountInfo.AccountId.ToString();
}
return string.Empty;
}
[SuppressMessage("", "VSTHRD100")]
private async void OnRootLoaded(object sender, RoutedEventArgs e)
{
@@ -54,17 +77,8 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
// Get user id from text input, login_uid is missed in cookie
string uid = UidInputText.Text;
if (uid.Length != 9)
{
await ThreadHelper.SwitchToMainThreadAsync();
infoBarService.Information(SH.ViewPageLoginHoyoverseUserHint);
return;
}
Cookie loginTicketCookie = Cookie.FromCoreWebView2Cookies(cookies);
string uid = await GetUidFromCookieAsync(Ioc.Default, loginTicketCookie, token).ConfigureAwait(false);
loginTicketCookie[Cookie.LOGIN_UID] = uid;
// 使用 loginTicket 获取 stoken
@@ -89,34 +103,71 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
switch (result)
{
case UserOptionResult.Added:
ViewModel.User.UserViewModel vm = Ioc.Default.GetRequiredService<ViewModel.User.UserViewModel>();
if (vm.Users!.Count == 1)
{
await ThreadHelper.SwitchToMainThreadAsync();
vm.SelectedUser = vm.Users.Single();
}
infoBarService.Success(string.Format(SH.ViewModelUserAdded, nickname));
break;
case UserOptionResult.Incomplete:
infoBarService.Information(SH.ViewModelUserIncomplete);
break;
case UserOptionResult.Invalid:
infoBarService.Information(SH.ViewModelUserInvalid);
break;
case UserOptionResult.Updated:
infoBarService.Success(string.Format(SH.ViewModelUserUpdated, nickname));
break;
default:
throw Must.NeverHappen();
}
await Ioc.Default
.GetRequiredService<ViewModel.User.UserViewModel>()
.HandleUserOptionResultAsync(result, nickname)
.ConfigureAwait(false);
}
private void CookieButtonClick(object sender, RoutedEventArgs e)
{
HandleCurrentCookieAsync().SafeForget();
}
private sealed class WebApiResponse<TData>
{
[JsonPropertyName("code")]
public int Code { get; set; }
[JsonPropertyName("data")]
public TData Data { get; set; } = default!;
}
private sealed class AccountInfoWrapper
{
[JsonPropertyName("account_info")]
public AccountInfo AccountInfo { get; set; } = default!;
[JsonPropertyName("game_ctrl_info")]
public JsonElement GameControlInfo { get; set; }
[JsonPropertyName("info")]
public string Info { get; set; } = default!;
[JsonPropertyName("msg")]
public string Message { get; set; } = default!;
[JsonPropertyName("notice_info")]
public JsonElement NoticeInfo { get; set; }
[JsonPropertyName("status")]
public int Status { get; set; }
}
private sealed class AccountInfo
{
[JsonPropertyName("account_id")]
public int AccountId { get; set; }
[JsonPropertyName("account_name")]
public string AccountName { get; set; } = default!;
[JsonPropertyName("area_code")]
public string AreaCode { get; set; } = default!;
[JsonPropertyName("country")]
public string Country { get; set; } = default!;
[JsonPropertyName("email")]
public string Email { get; set; } = default!;
[JsonPropertyName("mobile")]
public string Mobile { get; set; } = default!;
[JsonPropertyName("safe_level")]
public int SafeLevel { get; set; }
[JsonPropertyName("weblogin_token")]
public string WebLoginToken { get; set; } = default!;
}
}

View File

@@ -23,9 +23,6 @@
HorizontalAlignment="Right"
Click="CookieButtonClick"
Content="{shcm:ResourceString Name=ViewPageLoginMihoyoUserLoggedInAction}"/>
<WebView2
x:Name="WebView"
Grid.Row="2"
Margin="0,0,0,0"/>
<WebView2 x:Name="WebView" Grid.Row="2"/>
</Grid>
</Page>

View File

@@ -86,33 +86,11 @@ internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.P
.ConfigureAwait(false);
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
// TODO: Move these code somewhere else.
switch (result)
{
case UserOptionResult.Added:
ViewModel.User.UserViewModel vm = Ioc.Default.GetRequiredService<ViewModel.User.UserViewModel>();
if (vm.Users!.Count == 1)
{
await ThreadHelper.SwitchToMainThreadAsync();
vm.SelectedUser = vm.Users.Single();
}
infoBarService.Success(string.Format(SH.ViewModelUserAdded, nickname));
break;
case UserOptionResult.Incomplete:
infoBarService.Information(SH.ViewModelUserIncomplete);
break;
case UserOptionResult.Invalid:
infoBarService.Information(SH.ViewModelUserInvalid);
break;
case UserOptionResult.Updated:
infoBarService.Success(string.Format(SH.ViewModelUserUpdated, nickname));
break;
default:
throw Must.NeverHappen();
}
await Ioc.Default
.GetRequiredService<ViewModel.User.UserViewModel>()
.HandleUserOptionResultAsync(result, nickname)
.ConfigureAwait(false);
}
private void CookieButtonClick(object sender, RoutedEventArgs e)

View File

@@ -117,6 +117,39 @@ internal sealed class UserViewModel : ObservableObject
/// </summary>
public ICommand RefreshCookieTokenCommand { get; }
/// <summary>
/// 处理用户操作结果
/// </summary>
/// <param name="optionResult">操作结果</param>
/// <param name="uid">uid</param>
/// <returns>任务</returns>
public async Task HandleUserOptionResultAsync(UserOptionResult optionResult, string uid)
{
switch (optionResult)
{
case UserOptionResult.Added:
if (Users!.Count == 1)
{
await ThreadHelper.SwitchToMainThreadAsync();
SelectedUser = Users.Single();
}
infoBarService.Success(string.Format(SH.ViewModelUserAdded, uid));
break;
case UserOptionResult.Incomplete:
infoBarService.Information(SH.ViewModelUserIncomplete);
break;
case UserOptionResult.Invalid:
infoBarService.Information(SH.ViewModelUserInvalid);
break;
case UserOptionResult.Updated:
infoBarService.Success(string.Format(SH.ViewModelUserUpdated, uid));
break;
default:
throw Must.NeverHappen();
}
}
private async Task OpenUIAsync()
{
try
@@ -155,29 +188,7 @@ internal sealed class UserViewModel : ObservableObject
(UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(cookie, isOversea).ConfigureAwait(false);
switch (optionResult)
{
case UserOptionResult.Added:
if (Users!.Count == 1)
{
await ThreadHelper.SwitchToMainThreadAsync();
SelectedUser = Users.Single();
}
infoBarService.Success(string.Format(SH.ViewModelUserAdded, uid));
break;
case UserOptionResult.Incomplete:
infoBarService.Information(SH.ViewModelUserIncomplete);
break;
case UserOptionResult.Invalid:
infoBarService.Information(SH.ViewModelUserInvalid);
break;
case UserOptionResult.Updated:
infoBarService.Success(string.Format(SH.ViewModelUserUpdated, uid));
break;
default:
throw Must.NeverHappen();
}
await HandleUserOptionResultAsync(optionResult, uid).ConfigureAwait(false);
}
}

View File

@@ -233,6 +233,14 @@ internal static class ApiOsEndpoints
}
#endregion
#region WebApiOsAccountApi
/// <summary>
/// 使用 Cookie 登录
/// </summary>
public const string WebApiOsAccountLoginByCookie = $"{WebApiOsAccountApi}/login_by_cookie";
#endregion
#region Hosts | Queries
private const string ApiNaGeetest = "https://api-na.geetest.com";
@@ -254,6 +262,9 @@ internal static class ApiOsEndpoints
private const string SgPublicApi = "https://sg-public-api.hoyolab.com";
private const string WebApiOs = "https://webapi-os.account.hoyoverse.com";
private const string WebApiOsAccountApi = $"{WebApiOs}/Api";
/// <summary>
/// Web static referer
/// </summary>