mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
fix HoYoLAB webview login
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user