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
|
<Identity
|
||||||
Name="60568DGPStudio.SnapHutao"
|
Name="60568DGPStudio.SnapHutao"
|
||||||
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
Publisher="CN=35C8E923-85DF-49A7-9172-B39DC6312C52"
|
||||||
Version="1.5.4.0" />
|
Version="1.6.0.0" />
|
||||||
|
|
||||||
<Properties>
|
<Properties>
|
||||||
<DisplayName>Snap Hutao</DisplayName>
|
<DisplayName>Snap Hutao</DisplayName>
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ internal class UserService : IUserService
|
|||||||
public async Task<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(Cookie cookie, bool isOversea)
|
public async Task<ValueResult<UserOptionResult, string>> ProcessInputCookieAsync(Cookie cookie, bool isOversea)
|
||||||
{
|
{
|
||||||
await ThreadHelper.SwitchToBackgroundAsync();
|
await ThreadHelper.SwitchToBackgroundAsync();
|
||||||
string? mid = cookie.GetValueOrDefault(isOversea ? Cookie.MID : Cookie.STUID);
|
string? mid = cookie.GetValueOrDefault(isOversea ? Cookie.STUID : Cookie.MID);
|
||||||
|
|
||||||
if (mid == null)
|
if (mid == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -21,7 +21,12 @@
|
|||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
PlaceholderText="{shcm:ResourceString Name=ViewDialogUserInputPlaceholder}"
|
PlaceholderText="{shcm:ResourceString Name=ViewDialogUserInputPlaceholder}"
|
||||||
TextChanged="InputTextChanged"/>
|
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
|
<clw:SettingsCard
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
Description="{shcm:ResourceString Name=ViewDialogUserDocumentDescription}"
|
Description="{shcm:ResourceString Name=ViewDialogUserDocumentDescription}"
|
||||||
|
|||||||
@@ -18,33 +18,11 @@
|
|||||||
Margin="12,0,0,0"
|
Margin="12,0,0,0"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Text="{shcm:ResourceString Name=ViewPageLoginMihoyoUserTitle}"/>
|
Text="{shcm:ResourceString Name=ViewPageLoginMihoyoUserTitle}"/>
|
||||||
<Grid
|
<Button
|
||||||
Grid.Row="0"
|
|
||||||
Margin="16"
|
Margin="16"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
VerticalAlignment="Center">
|
Click="CookieButtonClick"
|
||||||
<Grid.ColumnDefinitions>
|
Content="{shcm:ResourceString Name=ViewPageLoginMihoyoUserLoggedInAction}"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<WebView2 x:Name="WebView" Grid.Row="2"/>
|
||||||
<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"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -6,9 +6,12 @@ using Microsoft.Web.WebView2.Core;
|
|||||||
using Snap.Hutao.Service.Abstraction;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Service.Navigation;
|
using Snap.Hutao.Service.Navigation;
|
||||||
using Snap.Hutao.Service.User;
|
using Snap.Hutao.Service.User;
|
||||||
|
using Snap.Hutao.Web;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
|
||||||
|
using Snap.Hutao.Web.Request;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
namespace Snap.Hutao.View.Page;
|
namespace Snap.Hutao.View.Page;
|
||||||
|
|
||||||
@@ -25,6 +28,26 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
|
|||||||
InitializeComponent();
|
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")]
|
[SuppressMessage("", "VSTHRD100")]
|
||||||
private async void OnRootLoaded(object sender, RoutedEventArgs e)
|
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>();
|
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);
|
Cookie loginTicketCookie = Cookie.FromCoreWebView2Cookies(cookies);
|
||||||
|
string uid = await GetUidFromCookieAsync(Ioc.Default, loginTicketCookie, token).ConfigureAwait(false);
|
||||||
loginTicketCookie[Cookie.LOGIN_UID] = uid;
|
loginTicketCookie[Cookie.LOGIN_UID] = uid;
|
||||||
|
|
||||||
// 使用 loginTicket 获取 stoken
|
// 使用 loginTicket 获取 stoken
|
||||||
@@ -89,34 +103,71 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
|
|||||||
|
|
||||||
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
|
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
|
||||||
|
|
||||||
switch (result)
|
await Ioc.Default
|
||||||
{
|
.GetRequiredService<ViewModel.User.UserViewModel>()
|
||||||
case UserOptionResult.Added:
|
.HandleUserOptionResultAsync(result, nickname)
|
||||||
ViewModel.User.UserViewModel vm = Ioc.Default.GetRequiredService<ViewModel.User.UserViewModel>();
|
.ConfigureAwait(false);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CookieButtonClick(object sender, RoutedEventArgs e)
|
private void CookieButtonClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
HandleCurrentCookieAsync().SafeForget();
|
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"
|
HorizontalAlignment="Right"
|
||||||
Click="CookieButtonClick"
|
Click="CookieButtonClick"
|
||||||
Content="{shcm:ResourceString Name=ViewPageLoginMihoyoUserLoggedInAction}"/>
|
Content="{shcm:ResourceString Name=ViewPageLoginMihoyoUserLoggedInAction}"/>
|
||||||
<WebView2
|
<WebView2 x:Name="WebView" Grid.Row="2"/>
|
||||||
x:Name="WebView"
|
|
||||||
Grid.Row="2"
|
|
||||||
Margin="0,0,0,0"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
@@ -86,33 +86,11 @@ internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.P
|
|||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
|
Ioc.Default.GetRequiredService<INavigationService>().GoBack();
|
||||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
|
||||||
|
|
||||||
// TODO: Move these code somewhere else.
|
await Ioc.Default
|
||||||
switch (result)
|
.GetRequiredService<ViewModel.User.UserViewModel>()
|
||||||
{
|
.HandleUserOptionResultAsync(result, nickname)
|
||||||
case UserOptionResult.Added:
|
.ConfigureAwait(false);
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CookieButtonClick(object sender, RoutedEventArgs e)
|
private void CookieButtonClick(object sender, RoutedEventArgs e)
|
||||||
|
|||||||
@@ -117,6 +117,39 @@ internal sealed class UserViewModel : ObservableObject
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ICommand RefreshCookieTokenCommand { get; }
|
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()
|
private async Task OpenUIAsync()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -155,29 +188,7 @@ internal sealed class UserViewModel : ObservableObject
|
|||||||
|
|
||||||
(UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(cookie, isOversea).ConfigureAwait(false);
|
(UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(cookie, isOversea).ConfigureAwait(false);
|
||||||
|
|
||||||
switch (optionResult)
|
await HandleUserOptionResultAsync(optionResult, uid).ConfigureAwait(false);
|
||||||
{
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,14 @@ internal static class ApiOsEndpoints
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region WebApiOsAccountApi
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用 Cookie 登录
|
||||||
|
/// </summary>
|
||||||
|
public const string WebApiOsAccountLoginByCookie = $"{WebApiOsAccountApi}/login_by_cookie";
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Hosts | Queries
|
#region Hosts | Queries
|
||||||
private const string ApiNaGeetest = "https://api-na.geetest.com";
|
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 SgPublicApi = "https://sg-public-api.hoyolab.com";
|
||||||
|
|
||||||
|
private const string WebApiOs = "https://webapi-os.account.hoyoverse.com";
|
||||||
|
private const string WebApiOsAccountApi = $"{WebApiOs}/Api";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Web static referer
|
/// Web static referer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user