Merge pull request #690 from Xhichn/SignIn

Feat: Daily check-in support for hoyolab user
This commit is contained in:
DismissedLight
2023-04-19 21:28:07 +08:00
committed by GitHub
9 changed files with 105 additions and 23 deletions

View File

@@ -47,7 +47,7 @@ internal static class CoreEnvironment
/// <summary>
/// Hoyolab Rpc 版本
/// </summary>
public const string HoyolabOsXrpcVersion = "2.28.0";
public const string HoyolabOsXrpcVersion = "2.30.0";
/// <summary>
/// 盐

View File

@@ -34,7 +34,7 @@ public static partial class Program
// by adding the using statement, we can dispose the injected services when we closing
using (ServiceProvider serviceProvider = InitializeDependencyInjection())
{
InitializeCulture(serviceProvider.GetRequiredService<AppOptions>().CurrentCulture);
InitializeCulture(serviceProvider);
// In a Desktop app this runs a message pump internally,
// and does not return until the application shuts down.
@@ -49,8 +49,13 @@ public static partial class Program
_ = Ioc.Default.GetRequiredService<App>();
}
private static void InitializeCulture(CultureInfo cultureInfo)
private static void InitializeCulture(IServiceProvider serviceProvider)
{
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
appOptions.PreviousCulture = CultureInfo.CurrentCulture;
CultureInfo cultureInfo = appOptions.CurrentCulture;
CultureInfo.CurrentCulture = cultureInfo;
CultureInfo.CurrentUICulture = cultureInfo;
ApplicationLanguages.PrimaryLanguageOverride = cultureInfo.Name;

View File

@@ -55,6 +55,12 @@ internal sealed class AppOptions : DbStoreOptions
set => SetOption(ref backdropType, SettingEntry.SystemBackdropType, value, value => value.ToString());
}
/// <summary>
/// 初始化前的语言
/// 通过设置与获取此属性,就可以获取到与系统同步的语言
/// </summary>
public CultureInfo PreviousCulture { get; set; } = default!;
/// <summary>
/// 当前语言
/// </summary>

View File

@@ -11,7 +11,7 @@ using Snap.Hutao.Web.Bridge;
namespace Snap.Hutao.View.Dialog;
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ի<EFBFBD><EFBFBD><EFBFBD>
/// 养成计算器对话框
/// </summary>
[HighQuality]
internal sealed partial class AdoptCalculatorDialog : ContentDialog
@@ -20,9 +20,9 @@ internal sealed partial class AdoptCalculatorDialog : ContentDialog
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ի<EFBFBD><EFBFBD><EFBFBD>
/// 构造一个新的养成计算器对话框
/// </summary>
/// <param name="window"><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
/// <param name="window">窗体</param>
public AdoptCalculatorDialog()
{
InitializeComponent();

View File

@@ -11,7 +11,7 @@ using Snap.Hutao.Web.Bridge;
namespace Snap.Hutao.View.Dialog;
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><EFBFBD>¼<EFBFBD>Ի<EFBFBD><EFBFBD><EFBFBD>
/// 社区游戏记录对话框
/// </summary>
[HighQuality]
internal sealed partial class CommunityGameRecordDialog : ContentDialog
@@ -20,9 +20,9 @@ internal sealed partial class CommunityGameRecordDialog : ContentDialog
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// <EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><EFBFBD>¼<EFBFBD>Ի<EFBFBD><EFBFBD><EFBFBD>
/// 构造一个新的社区游戏记录对话框
/// </summary>
/// <param name="window"><EFBFBD><EFBFBD><EFBFBD><EFBFBD></param>
/// <param name="window">窗体</param>
public CommunityGameRecordDialog()
{
InitializeComponent();

View File

@@ -17,12 +17,11 @@ internal sealed partial class SignInWebViewDialog : ContentDialog
{
private readonly IServiceScope scope;
[SuppressMessage("", "IDE0052")]
private SignInJsInterface? signInJsInterface;
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// 构造一个新的签到网页视图对话框
/// </summary>
/// <param name="window">窗口</param>
public SignInWebViewDialog()
{
InitializeComponent();
@@ -48,21 +47,21 @@ internal sealed partial class SignInWebViewDialog : ContentDialog
if (user.Entity.IsOversea)
{
coreWebView2.SetCookie(user.CookieToken, user.LToken, null).SetMobileOverseaUserAgent();
signInJsInterface = new(coreWebView2, scope.ServiceProvider);
coreWebView2.SetCookie(user.CookieToken, user.LToken, null, true).SetMobileOverseaUserAgent();
jsInterface = new SignInJSInterfaceOversea(coreWebView2, scope.ServiceProvider);
coreWebView2.Navigate("https://act.hoyolab.com/ys/event/signin-sea-v3/index.html?act_id=e202102251931481");
}
else
{
coreWebView2.SetCookie(user.CookieToken, user.LToken, null).SetMobileUserAgent();
signInJsInterface = new(coreWebView2, scope.ServiceProvider);
coreWebView2.SetCookie(user.CookieToken, user.LToken, null, false).SetMobileUserAgent();
jsInterface = new SignInJsInterface(coreWebView2, scope.ServiceProvider);
coreWebView2.Navigate("https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?act_id=e202009291139501");
}
}
private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
{
signInJsInterface = null;
jsInterface = null;
scope.Dispose();
}
}

View File

@@ -41,32 +41,34 @@ internal static class CoreWebView2Extension
/// <param name="cookieToken">CookieToken</param>
/// <param name="lToken">LToken</param>
/// <param name="sToken">SToken</param>
/// <param name="isOversea">是否为国际服,用于改变 cookie domain</param>
/// <returns>链式调用的WebView2</returns>
public static CoreWebView2 SetCookie(this CoreWebView2 webView, Cookie? cookieToken = null, Cookie? lToken = null, Cookie? sToken = null)
public static CoreWebView2 SetCookie(this CoreWebView2 webView, Cookie? cookieToken = null, Cookie? lToken = null, Cookie? sToken = null, bool isOversea = false)
{
CoreWebView2CookieManager cookieManager = webView.CookieManager;
if (cookieToken != null)
{
cookieManager.AddMihoyoCookie("account_id", cookieToken).AddMihoyoCookie("cookie_token", cookieToken);
cookieManager.AddMihoyoCookie(Cookie.ACCOUNT_ID, cookieToken, isOversea).AddMihoyoCookie(Cookie.COOKIE_TOKEN, cookieToken, isOversea);
}
if (lToken != null)
{
cookieManager.AddMihoyoCookie("ltuid", lToken).AddMihoyoCookie("ltoken", lToken);
cookieManager.AddMihoyoCookie(Cookie.LTUID, lToken, isOversea).AddMihoyoCookie(Cookie.LTOKEN, lToken, isOversea);
}
if (sToken != null)
{
cookieManager.AddMihoyoCookie("stuid", sToken).AddMihoyoCookie("stoken", sToken);
cookieManager.AddMihoyoCookie(Cookie.STUID, sToken, isOversea).AddMihoyoCookie(Cookie.STOKEN, sToken, isOversea);
}
return webView;
}
private static CoreWebView2CookieManager AddMihoyoCookie(this CoreWebView2CookieManager manager, string name, Cookie cookie)
private static CoreWebView2CookieManager AddMihoyoCookie(this CoreWebView2CookieManager manager, string name, Cookie cookie, bool isOversea = false)
{
manager.AddOrUpdateCookie(manager.CreateCookie(name, cookie[name], ".mihoyo.com", "/"));
string domain = isOversea ? ".hoyolab.com" : ".mihoyo.com";
manager.AddOrUpdateCookie(manager.CreateCookie(name, cookie[name], domain, "/"));
return manager;
}
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service;
using Snap.Hutao.Service.User;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Bridge.Model;
@@ -9,6 +10,7 @@ using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Bbs.User;
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
@@ -148,6 +150,7 @@ internal class MiHoYoJSInterface
/// <returns>响应</returns>
public virtual JsResult<Dictionary<string, string>> GetDynamicSecrectV2(JsParam<DynamicSecrect2Playload> param)
{
// TODO: Salt X4 for hoyolab user
string salt = Core.CoreEnvironment.DynamicSecretSalts[SaltType.X4];
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
int r = GetRandom();
@@ -253,6 +256,25 @@ internal class MiHoYoJSInterface
return null;
}
/// <summary>
/// 获取当前语言和时区
/// </summary>
/// <param name="param">param</param>
/// <returns>语言与时区</returns>
public virtual JsResult<Dictionary<string, string>> GetCurrentLocale(JsParam<PushPagePayload> param)
{
AppOptions appOptions = serviceProvider.GetRequiredService<AppOptions>();
return new()
{
Data = new()
{
["language"] = appOptions.PreviousCulture.Name.ToLowerInvariant(),
["timeZone"] = "GMT+8",
},
};
}
public virtual Task<IJsResult?> ShowAlertDialogAsync(JsParam param)
{
return Task.FromException<IJsResult?>(new NotImplementedException());
@@ -369,6 +391,7 @@ internal class MiHoYoJSInterface
"getActionTicket" => await GetActionTicketAsync(param).ConfigureAwait(false),
"getCookieInfo" => GetCookieInfo(param),
"getCookieToken" => await GetCookieTokenAsync(param).ConfigureAwait(false),
"getCurrentLocale" => GetCurrentLocale(param),
"getDS" => GetDynamicSecrectV1(param),
"getDS2" => GetDynamicSecrectV2(param),
"getHTTPRequestHeaders" => GetHttpRequestHeader(param),
@@ -395,7 +418,8 @@ internal class MiHoYoJSInterface
private void OnNavigationStarting(CoreWebView2 coreWebView2, CoreWebView2NavigationStartingEventArgs args)
{
if (new Uri(args.Uri).Host.EndsWith("mihoyo.com"))
string uriHost = new Uri(args.Uri).Host;
if (uriHost.EndsWith("mihoyo.com") || uriHost.EndsWith("hoyolab.com"))
{
// Execute this solve issue: When open same site second time,there might be no bridge init.
coreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(InitializeJsInterfaceScript2).AsTask().SafeForget(logger);

View File

@@ -0,0 +1,46 @@
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Web.Bridge.Model;
namespace Snap.Hutao.Web.Bridge;
/// <summary>
/// HoYoLAB 签到页面JS桥
/// </summary>
[HighQuality]
internal sealed class SignInJSInterfaceOversea : MiHoYoJSInterface
{
private const string RemoveRotationWarningScript = """
let landscape = document.getElementById('mihoyo_landscape');
landscape.remove();
""";
private readonly ILogger<MiHoYoJSInterface> logger;
/// <inheritdoc cref="MiHoYoJSInterface(CoreWebView2, IServiceProvider)"/>
public SignInJSInterfaceOversea(CoreWebView2 webView, IServiceProvider serviceProvider)
: base(webView, serviceProvider)
{
logger = serviceProvider.GetRequiredService<ILogger<MiHoYoJSInterface>>();
webView.DOMContentLoaded += OnDOMContentLoaded;
}
/// <inheritdoc/>
public override JsResult<Dictionary<string, string>> GetHttpRequestHeader(JsParam param)
{
return new()
{
Data = new Dictionary<string, string>()
{
{ "x-rpc-client_type", "2" },
{ "x-rpc-device_id", Core.CoreEnvironment.HoyolabDeviceId },
{ "x-rpc-app_version", Core.CoreEnvironment.HoyolabOsXrpcVersion },
},
};
}
private void OnDOMContentLoaded(CoreWebView2 coreWebView2, CoreWebView2DOMContentLoadedEventArgs args)
{
// 移除“请旋转手机”提示所在的HTML元素
coreWebView2.ExecuteScriptAsync(RemoveRotationWarningScript).AsTask().SafeForget(logger);
}
}