From 9f793670feaa7da3d0c8f1c6d819d2e8fca7159f Mon Sep 17 00:00:00 2001
From: DismissedLight <1686188646@qq.com>
Date: Thu, 14 Dec 2023 22:47:17 +0800
Subject: [PATCH] failed attempt: fight with device_fp
---
.../BaseClassLibrary/JsonSerializeTest.cs | 3 +-
.../Core/Abstraction/IDeconstruct.cs | 16 +--
.../DependencyInjection.cs | 2 +-
.../EnumerableExtension.Dictionary.cs | 25 ----
.../Extension/EnumerableExtension.cs | 61 ++++++++-
.../Model/Entity/Extension/UserExtension.cs | 19 +++
.../Snap.Hutao/Service/AppOptions.cs | 2 -
.../Snap.Hutao/Service/AppOptionsExtension.cs | 3 -
.../Game/PathAbstraction/GamePathService.cs | 1 -
.../Service/User/IUserCollectionService.cs | 3 +-
.../User/IUserInitializationService.cs | 4 +-
.../Snap.Hutao/Service/User/IUserService.cs | 9 +-
.../Snap.Hutao/Service/User/InputCookie.cs | 48 +++++++
.../Service/User/UserCollectionService.cs | 10 +-
.../Service/User/UserFingerprintService.cs | 6 +-
.../Service/User/UserInitializationService.cs | 5 +-
.../Snap.Hutao/Service/User/UserService.cs | 15 +-
src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj | 1 +
.../View/Page/ISupportLoginByWebView.cs | 5 +-
.../View/Page/LoginHoyoverseUserPage.xaml.cs | 3 +-
.../View/Page/LoginMihoyoUserPage.xaml.cs | 4 +-
.../ViewModel/Setting/SettingViewModel.cs | 1 -
.../Snap.Hutao/ViewModel/User/User.cs | 4 +-
.../ViewModel/User/UserExtension.cs | 14 ++
.../ViewModel/User/UserViewModel.cs | 4 +-
.../Snap.Hutao/Web/Hoyolab/Cookie.cs | 128 +-----------------
.../Snap.Hutao/Web/Hoyolab/CookieExtension.cs | 68 ++++++++++
.../Web/Hoyolab/Passport/PassportClient2.cs | 1 -
28 files changed, 255 insertions(+), 210 deletions(-)
create mode 100644 src/Snap.Hutao/Snap.Hutao/Model/Entity/Extension/UserExtension.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Service/User/InputCookie.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserExtension.cs
create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieExtension.cs
diff --git a/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs b/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs
index 42b0ca1d..309ef2bb 100644
--- a/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs
+++ b/src/Snap.Hutao/Snap.Hutao.Test/BaseClassLibrary/JsonSerializeTest.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IDeconstruct.cs b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IDeconstruct.cs
index be6f3181..a0052c17 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IDeconstruct.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/Abstraction/IDeconstruct.cs
@@ -3,18 +3,12 @@
namespace Snap.Hutao.Core.Abstraction;
-///
-/// 指示该类可以解构为元组
-///
-/// 元组的第一个类型
-/// 元组的第二个类型
-[HighQuality]
internal interface IDeconstruct
{
- ///
- /// 解构
- ///
- /// 第一个元素
- /// 第二个元素
void Deconstruct(out T1 t1, out T2 t2);
+}
+
+internal interface IDeconstruct
+{
+ void Deconstruct(out T1 t1, out T2 t2, out T3 t3);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs
index 1f515abd..7799061a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/DependencyInjection.cs
@@ -24,7 +24,7 @@ internal static class DependencyInjection
ServiceProvider serviceProvider = new ServiceCollection()
// Microsoft extension
- .AddLogging(builder => builder.AddConsoleWindow())
+ .AddLogging(builder => builder.AddDebug().AddConsoleWindow())
.AddMemoryCache()
// Hutao extensions
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs
index c6897d7f..bfd640f5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.Dictionary.cs
@@ -23,13 +23,6 @@ internal static partial class EnumerableExtension
return true;
}
- ///
- /// 增加计数
- ///
- /// 键类型
- /// 值类型
- /// 字典
- /// 键
public static void IncreaseOne(this Dictionary dict, TKey key)
where TKey : notnull
where TValue : struct, IIncrementOperators
@@ -37,14 +30,6 @@ internal static partial class EnumerableExtension
++CollectionsMarshal.GetValueRefOrAddDefault(dict, key, out _);
}
- ///
- /// 增加计数
- ///
- /// 键类型
- /// 值类型
- /// 字典
- /// 键
- /// 增加的值
public static void IncreaseValue(this Dictionary dict, TKey key, TValue value)
where TKey : notnull
where TValue : struct, IAdditionOperators
@@ -54,14 +39,6 @@ internal static partial class EnumerableExtension
current += value;
}
- ///
- /// 增加计数
- ///
- /// 键类型
- /// 值类型
- /// 字典
- /// 键
- /// 是否存在键值
public static bool TryIncreaseOne(this Dictionary dict, TKey key)
where TKey : notnull
where TValue : struct, IIncrementOperators
@@ -76,7 +53,6 @@ internal static partial class EnumerableExtension
return false;
}
- ///
public static Dictionary ToDictionaryIgnoringDuplicateKeys(this IEnumerable source, Func keySelector)
where TKey : notnull
{
@@ -90,7 +66,6 @@ internal static partial class EnumerableExtension
return dictionary;
}
- ///
public static Dictionary ToDictionaryIgnoringDuplicateKeys(this IEnumerable source, Func keySelector, Func elementSelector)
where TKey : notnull
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs
index 3ff7c070..a9a99041 100644
--- a/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Extension/EnumerableExtension.cs
@@ -3,6 +3,7 @@
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
+using System.Text;
namespace Snap.Hutao.Extension;
@@ -43,6 +44,63 @@ internal static partial class EnumerableExtension
return first;
}
+ public static string JoinToString(this IEnumerable source, char separator, Action selector)
+ {
+ StringBuilder resultBuilder = new();
+
+ IEnumerator enumerator = source.GetEnumerator();
+ if (!enumerator.MoveNext())
+ {
+ return string.Empty;
+ }
+
+ T first = enumerator.Current;
+ selector(resultBuilder, first);
+
+ if (!enumerator.MoveNext())
+ {
+ return resultBuilder.ToString();
+ }
+
+ do
+ {
+ resultBuilder.Append(separator);
+ selector(resultBuilder, enumerator.Current);
+ }
+ while (enumerator.MoveNext());
+
+ return resultBuilder.ToString();
+ }
+
+ public static string JoinToString(this IEnumerable> source, char separator, Action selector)
+ {
+ StringBuilder resultBuilder = new();
+
+ IEnumerator> enumerator = source.GetEnumerator();
+ if (!enumerator.MoveNext())
+ {
+ return string.Empty;
+ }
+
+ KeyValuePair first = enumerator.Current;
+ selector(resultBuilder, first.Key, first.Value);
+
+ if (!enumerator.MoveNext())
+ {
+ return resultBuilder.ToString();
+ }
+
+ do
+ {
+ resultBuilder.Append(separator);
+ KeyValuePair current = enumerator.Current;
+ selector(resultBuilder, current.Key, current.Value);
+ }
+ while (enumerator.MoveNext());
+
+ return resultBuilder.ToString();
+ }
+
///
/// 转换到
///
@@ -64,7 +122,6 @@ internal static partial class EnumerableExtension
/// Converted collection into string.
public static string ToString(this IEnumerable collection, char separator)
{
- string result = string.Join(separator, collection);
- return result.Length > 0 ? result : string.Empty;
+ return string.Join(separator, collection);
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Extension/UserExtension.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Extension/UserExtension.cs
new file mode 100644
index 00000000..66f2df90
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Extension/UserExtension.cs
@@ -0,0 +1,19 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Entity.Extension;
+
+internal static class UserExtension
+{
+ public static bool TryUpdateFingerprint(this User user, string? deviceFp)
+ {
+ if (string.IsNullOrEmpty(deviceFp))
+ {
+ return false;
+ }
+
+ user.Fingerprint = deviceFp;
+ user.FingerprintLastUpdateTime = DateTimeOffset.UtcNow;
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
index 8c9adcfa..aeb46ccc 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptions.cs
@@ -6,8 +6,6 @@ using Snap.Hutao.Core.Windowing;
using Snap.Hutao.Model;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Service.Abstraction;
-using Snap.Hutao.Service.Game.PathAbstraction;
-using System.Collections.Immutable;
using System.Globalization;
using System.IO;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs b/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs
index 34c731f1..d05ac848 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/AppOptionsExtension.cs
@@ -2,10 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Model;
-using Snap.Hutao.Service.Game.PathAbstraction;
-using System.Collections.Immutable;
using System.Globalization;
-using System.IO;
namespace Snap.Hutao.Service;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Game/PathAbstraction/GamePathService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Game/PathAbstraction/GamePathService.cs
index c1c9eff4..a9294f1d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Game/PathAbstraction/GamePathService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Game/PathAbstraction/GamePathService.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.Service.Game.Locator;
-using Snap.Hutao.Service.Game.PathAbstraction;
namespace Snap.Hutao.Service.Game.PathAbstraction;
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserCollectionService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserCollectionService.cs
index 9d914db6..b34c8e67 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserCollectionService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserCollectionService.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.ViewModel.User;
-using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using System.Collections.ObjectModel;
using BindingUser = Snap.Hutao.ViewModel.User.User;
@@ -21,7 +20,7 @@ internal interface IUserCollectionService
ValueTask RemoveUserAsync(BindingUser user);
- ValueTask> TryCreateAndAddUserFromCookieAsync(Cookie cookie, bool isOversea);
+ ValueTask> TryCreateAndAddUserFromInputCookieAsync(InputCookie inputCookie);
bool TryGetUserByMid(string mid, [NotNullWhen(true)] out BindingUser? user);
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs
index 03f62a30..442bd68b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserInitializationService.cs
@@ -1,13 +1,11 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Web.Hoyolab;
-
namespace Snap.Hutao.Service.User;
internal interface IUserInitializationService
{
- ValueTask CreateUserFromCookieOrDefaultAsync(Cookie cookie, bool isOversea, CancellationToken token = default(CancellationToken));
+ ValueTask CreateUserFromInputCookieOrDefaultAsync(InputCookie inputCookie, CancellationToken token = default(CancellationToken));
ValueTask ResumeUserAsync(Model.Entity.User inner, CancellationToken token = default(CancellationToken));
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs
index e89d0494..9174bb80 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license.
using Snap.Hutao.ViewModel.User;
-using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using System.Collections.ObjectModel;
using BindingUser = Snap.Hutao.ViewModel.User.User;
@@ -42,13 +41,7 @@ internal interface IUserService
/// 对应的角色信息
UserGameRole? GetUserGameRoleByUid(string uid);
- ///
- /// 尝试异步处理输入的Cookie
- ///
- /// Cookie
- /// 是否为国际服
- /// 处理的结果
- ValueTask> ProcessInputCookieAsync(Cookie cookie, bool isOversea);
+ ValueTask> ProcessInputCookieAsync(InputCookie inputCookie);
ValueTask RefreshCookieTokenAsync(Model.Entity.User user);
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/InputCookie.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/InputCookie.cs
new file mode 100644
index 00000000..c09ba478
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/InputCookie.cs
@@ -0,0 +1,48 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Core.Abstraction;
+using Snap.Hutao.Web.Hoyolab;
+
+namespace Snap.Hutao.Service.User;
+
+internal sealed class InputCookie : IDeconstruct
+{
+ private InputCookie(Cookie cookie, bool isOversea)
+ {
+ Cookie = cookie;
+ IsOversea = isOversea;
+ cookie.TryGetDeviceFp(out string? deviceFp);
+ DeviceFp = deviceFp;
+ }
+
+ private InputCookie(Cookie cookie, bool isOversea, string? deviceFp)
+ {
+ Cookie = cookie;
+ IsOversea = isOversea;
+ DeviceFp = deviceFp;
+ }
+
+ public Cookie Cookie { get; }
+
+ public bool IsOversea { get; }
+
+ public string? DeviceFp { get; }
+
+ public static InputCookie Create(Cookie cookie, bool isOversea, string? deviceFp)
+ {
+ return new InputCookie(cookie, isOversea, deviceFp);
+ }
+
+ public static InputCookie CreateWithDeviceFpInference(Cookie cookie, bool isOversea)
+ {
+ return new InputCookie(cookie, isOversea);
+ }
+
+ public void Deconstruct(out Cookie cookie, out bool isOversea, out string? deviceFp)
+ {
+ cookie = Cookie;
+ isOversea = IsOversea;
+ deviceFp = DeviceFp;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserCollectionService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserCollectionService.cs
index 8c8fb108..6ea7d8ff 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserCollectionService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserCollectionService.cs
@@ -6,7 +6,6 @@ using Snap.Hutao.Core.Database;
using Snap.Hutao.Core.ExceptionService;
using Snap.Hutao.Message;
using Snap.Hutao.ViewModel.User;
-using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using System.Collections.ObjectModel;
using BindingUser = Snap.Hutao.ViewModel.User.User;
@@ -113,6 +112,11 @@ internal sealed partial class UserCollectionService : IUserCollectionService
midUserMap?.Remove(user.Entity.Mid);
}
+ foreach (UserGameRole role in user.UserGameRoles)
+ {
+ uidUserGameRoleMap?.Remove(role.GameUid);
+ }
+
// Sync database
await taskContext.SwitchToBackgroundAsync();
await userDbService.DeleteUserByIdAsync(user.Entity.InnerId).ConfigureAwait(false);
@@ -146,10 +150,10 @@ internal sealed partial class UserCollectionService : IUserCollectionService
return midUserMap.TryGetValue(mid, out user);
}
- public async ValueTask> TryCreateAndAddUserFromCookieAsync(Cookie cookie, bool isOversea)
+ public async ValueTask> TryCreateAndAddUserFromInputCookieAsync(InputCookie inputCookie)
{
await taskContext.SwitchToBackgroundAsync();
- BindingUser? newUser = await userInitializationService.CreateUserFromCookieOrDefaultAsync(cookie, isOversea).ConfigureAwait(false);
+ BindingUser? newUser = await userInitializationService.CreateUserFromInputCookieOrDefaultAsync(inputCookie).ConfigureAwait(false);
if (newUser is null)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs
index a8a1f59e..f119309d 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserFingerprintService.cs
@@ -1,6 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
+using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.PublicData.DeviceFp;
using Snap.Hutao.Web.Response;
@@ -21,7 +22,7 @@ internal sealed partial class UserFingerprintService : IUserFingerprintService
return;
}
- if (user.Entity.FingerprintLastUpdateTime >= DateTimeOffset.UtcNow - TimeSpan.FromDays(3))
+ if (user.Entity.FingerprintLastUpdateTime >= DateTimeOffset.UtcNow - TimeSpan.FromDays(7))
{
if (!string.IsNullOrEmpty(user.Fingerprint))
{
@@ -79,9 +80,8 @@ internal sealed partial class UserFingerprintService : IUserFingerprintService
};
Response response = await deviceFpClient.GetFingerprintAsync(data, token).ConfigureAwait(false);
- user.Fingerprint = response.IsOk() ? response.Data.DeviceFp : string.Empty;
+ user.TryUpdateFingerprint(response.IsOk() ? response.Data.DeviceFp : string.Empty);
- user.Entity.FingerprintLastUpdateTime = DateTimeOffset.UtcNow;
user.NeedDbUpdateAfterResume = true;
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs
index 08fc3785..02fce7b6 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserInitializationService.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Abstraction;
+using Snap.Hutao.Model.Entity.Extension;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Bbs.User;
using Snap.Hutao.Web.Hoyolab.Passport;
@@ -30,14 +31,16 @@ internal sealed partial class UserInitializationService : IUserInitializationSer
return user;
}
- public async ValueTask CreateUserFromCookieOrDefaultAsync(Cookie cookie, bool isOversea, CancellationToken token = default)
+ public async ValueTask CreateUserFromInputCookieOrDefaultAsync(InputCookie inputCookie, CancellationToken token = default)
{
// 这里只负责创建实体用户,稍后在用户服务中保存到数据库
+ (Cookie cookie, bool isOversea, string? deviceFp) = inputCookie;
Model.Entity.User entity = Model.Entity.User.From(cookie, isOversea);
entity.Aid = cookie.GetValueOrDefault(Cookie.STUID);
entity.Mid = isOversea ? entity.Aid : cookie.GetValueOrDefault(Cookie.MID);
entity.IsOversea = isOversea;
+ entity.TryUpdateFingerprint(deviceFp);
if (entity.Aid is not null && entity.Mid is not null)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
index 43b5e233..b42f23ab 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs
@@ -56,20 +56,22 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
return userCollectionService.GetUserGameRoleByUid(uid);
}
- public async ValueTask> ProcessInputCookieAsync(Cookie cookie, bool isOversea)
+ public async ValueTask> ProcessInputCookieAsync(InputCookie inputCookie)
{
await taskContext.SwitchToBackgroundAsync();
- string? mid = cookie.GetValueOrDefault(isOversea ? Cookie.STUID : Cookie.MID);
+ (Cookie cookie, bool isOversea, string? deviceFp) = inputCookie;
- if (string.IsNullOrEmpty(mid))
+ string? midOrAid = cookie.GetValueOrDefault(isOversea ? Cookie.STUID : Cookie.MID);
+
+ if (string.IsNullOrEmpty(midOrAid))
{
return new(UserOptionResult.CookieInvalid, SH.ServiceUserProcessCookieNoMid);
}
// 检查 mid 对应用户是否存在
- if (!userCollectionService.TryGetUserByMid(mid, out BindingUser? user))
+ if (!userCollectionService.TryGetUserByMid(midOrAid, out BindingUser? user))
{
- return await userCollectionService.TryCreateAndAddUserFromCookieAsync(cookie, isOversea).ConfigureAwait(false);
+ return await userCollectionService.TryCreateAndAddUserFromInputCookieAsync(inputCookie).ConfigureAwait(false);
}
if (!cookie.TryGetSToken(isOversea, out Cookie? stoken))
@@ -80,9 +82,10 @@ internal sealed partial class UserService : IUserService, IUserServiceUnsafe
user.SToken = stoken;
user.LToken = cookie.TryGetLToken(out Cookie? ltoken) ? ltoken : user.LToken;
user.CookieToken = cookie.TryGetCookieToken(out Cookie? cookieToken) ? cookieToken : user.CookieToken;
+ user.TryUpdateFingerprint(deviceFp);
await userDbService.UpdateUserAsync(user.Entity).ConfigureAwait(false);
- return new(UserOptionResult.CookieUpdated, mid);
+ return new(UserOptionResult.CookieUpdated, midOrAid);
}
public async ValueTask RefreshCookieTokenAsync(Model.Entity.User user)
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 615e07e0..36d2ffad 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -299,6 +299,7 @@
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/ISupportLoginByWebView.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/ISupportLoginByWebView.cs
index a6101a73..0a560cbd 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/ISupportLoginByWebView.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/ISupportLoginByWebView.cs
@@ -7,7 +7,6 @@ using Snap.Hutao.Service.Navigation;
using Snap.Hutao.Service.Notification;
using Snap.Hutao.Service.User;
using Snap.Hutao.Web.Bridge;
-using Snap.Hutao.Web.Hoyolab;
namespace Snap.Hutao.View.Page;
@@ -30,11 +29,11 @@ internal interface ISupportLoginByWebView
}
}
- static async ValueTask PostHandleCurrentCookieAsync(IServiceProvider serviceProvider, Cookie cookie, bool isOversea)
+ static async ValueTask PostHandleCurrentCookieAsync(IServiceProvider serviceProvider, InputCookie inputCookie)
{
(UserOptionResult result, string nickname) = await serviceProvider
.GetRequiredService()
- .ProcessInputCookieAsync(cookie, isOversea)
+ .ProcessInputCookieAsync(inputCookie)
.ConfigureAwait(false);
serviceProvider.GetRequiredService().GoBack();
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs
index 0ec71bf1..ef5deb5a 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginHoyoverseUserPage.xaml.cs
@@ -4,6 +4,7 @@
using Microsoft.UI.Xaml;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service.Notification;
+using Snap.Hutao.Service.User;
using Snap.Hutao.Web;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
@@ -88,7 +89,7 @@ internal sealed partial class LoginHoyoverseUserPage : Microsoft.UI.Xaml.Control
Cookie stokenV1 = Cookie.FromSToken(uid, multiTokenMap[Cookie.STOKEN]);
await ISupportLoginByWebView
- .PostHandleCurrentCookieAsync(serviceProvider, stokenV1, true)
+ .PostHandleCurrentCookieAsync(serviceProvider, InputCookie.Create(stokenV1, true, default))
.ConfigureAwait(false);
}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs
index c32a41d0..61529721 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/LoginMihoyoUserPage.xaml.cs
@@ -4,6 +4,7 @@
using Microsoft.UI.Xaml;
using Microsoft.Web.WebView2.Core;
using Snap.Hutao.Service.Notification;
+using Snap.Hutao.Service.User;
using Snap.Hutao.Web.Hoyolab;
using Snap.Hutao.Web.Hoyolab.Passport;
using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
@@ -73,9 +74,10 @@ internal sealed partial class LoginMihoyoUserPage : Microsoft.UI.Xaml.Controls.P
}
Cookie stokenV2 = Cookie.FromLoginResult(loginResultResponse.Data);
+ webCookie.TryGetDeviceFp(out string? deviceFp);
await ISupportLoginByWebView
- .PostHandleCurrentCookieAsync(serviceProvider, stokenV2, false)
+ .PostHandleCurrentCookieAsync(serviceProvider, InputCookie.Create(stokenV2, false, deviceFp))
.ConfigureAwait(false);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
index d72538c3..31f1defe 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/Setting/SettingViewModel.cs
@@ -1,7 +1,6 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Microsoft.Data.Sqlite;
using Microsoft.UI.Xaml.Controls;
using Microsoft.Windows.AppLifecycle;
using Snap.Hutao.Core;
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs
index 1d16a59a..06f3ba83 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/User.cs
@@ -55,7 +55,7 @@ internal sealed class User : ObservableObject, IEntityOnly, IMapping
set => SetSelectedUserGameRole(value);
}
- public string? Fingerprint { get => inner.Fingerprint; set => inner.Fingerprint = value; }
+ public string? Fingerprint { get => inner.Fingerprint; }
public Guid InnerId { get => inner.InnerId; }
@@ -111,4 +111,4 @@ internal sealed class User : ObservableObject, IEntityOnly, IMapping
messenger.Send(Message.UserChangedMessage.CreateOnlyRoleChanged(this));
}
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserExtension.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserExtension.cs
new file mode 100644
index 00000000..13dce0a8
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserExtension.cs
@@ -0,0 +1,14 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Entity.Extension;
+
+namespace Snap.Hutao.ViewModel.User;
+
+internal static class UserExtension
+{
+ public static bool TryUpdateFingerprint(this User user, string? deviceFp)
+ {
+ return user.Entity.TryUpdateFingerprint(deviceFp);
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs
index 129b7ff6..b0e46bc1 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/User/UserViewModel.cs
@@ -143,7 +143,7 @@ internal sealed partial class UserViewModel : ObservableObject
if (result.TryGetValue(out string rawCookie))
{
Cookie cookie = Cookie.Parse(rawCookie);
- (UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(cookie, isOversea).ConfigureAwait(false);
+ (UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(InputCookie.CreateWithDeviceFpInference(cookie, isOversea)).ConfigureAwait(false);
await HandleUserOptionResultAsync(optionResult, uid).ConfigureAwait(false);
}
}
@@ -192,7 +192,7 @@ internal sealed partial class UserViewModel : ObservableObject
if (sTokenResponse.IsOk())
{
Cookie stokenV2 = Cookie.FromLoginResult(sTokenResponse.Data);
- (UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(stokenV2, false).ConfigureAwait(false);
+ (UserOptionResult optionResult, string uid) = await userService.ProcessInputCookieAsync(InputCookie.CreateWithDeviceFpInference(stokenV2, false)).ConfigureAwait(false);
await HandleUserOptionResultAsync(optionResult, uid).ConfigureAwait(false);
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs
index 64309812..b3c94b14 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs
@@ -14,19 +14,12 @@ internal sealed partial class Cookie
{
private readonly SortedDictionary inner;
- ///
- /// 构造一个空白的Cookie
- ///
public Cookie()
: this([])
{
}
- ///
- /// 构造一个新的Cookie
- ///
- /// 源
- private Cookie(SortedDictionary dict)
+ public Cookie(SortedDictionary dict)
{
inner = dict;
}
@@ -37,11 +30,6 @@ internal sealed partial class Cookie
set => inner[key] = value;
}
- ///
- /// 解析Cookie字符串
- ///
- /// cookie字符串
- /// 新的Cookie对象
public static Cookie Parse(string cookieString)
{
SortedDictionary cookieMap = [];
@@ -69,11 +57,6 @@ internal sealed partial class Cookie
return new(cookieMap);
}
- ///
- /// 从登录结果创建 Cookie
- ///
- /// 登录结果
- /// Cookie
public static Cookie FromLoginResult(LoginResult? loginResult)
{
if (loginResult is null)
@@ -91,12 +74,6 @@ internal sealed partial class Cookie
return new(cookieMap);
}
- ///
- /// 从 SToken 创建 Cookie
- ///
- /// stuid
- /// stoken
- /// Cookie
public static Cookie FromSToken(string stuid, string stoken)
{
SortedDictionary cookieMap = new()
@@ -113,119 +90,18 @@ internal sealed partial class Cookie
return inner.Count <= 0;
}
- public bool TryGetLoginTicket([NotNullWhen(true)] out Cookie? cookie)
- {
- if (TryGetValue(LOGIN_TICKET, out string? loginTicket) && TryGetValue(LOGIN_UID, out string? loginUid))
- {
- cookie = new Cookie(new()
- {
- [LOGIN_TICKET] = loginTicket,
- [LOGIN_UID] = loginUid,
- });
-
- return true;
- }
-
- cookie = null;
- return false;
- }
-
- public bool TryGetSToken(bool isOversea, [NotNullWhen(true)] out Cookie? cookie)
- {
- return isOversea ? TryGetLegacySToken(out cookie) : TryGetSToken(out cookie);
- }
-
- public bool TryGetLToken([NotNullWhen(true)] out Cookie? cookie)
- {
- if (TryGetValue(LTOKEN, out string? ltoken) && TryGetValue(LTUID, out string? ltuid))
- {
- cookie = new Cookie(new()
- {
- [LTOKEN] = ltoken,
- [LTUID] = ltuid,
- });
-
- return true;
- }
-
- cookie = null;
- return false;
- }
-
- public bool TryGetCookieToken([NotNullWhen(true)] out Cookie? cookie)
- {
- if (TryGetValue(ACCOUNT_ID, out string? accountId) && TryGetValue(COOKIE_TOKEN, out string? cookieToken))
- {
- cookie = new Cookie(new()
- {
- [ACCOUNT_ID] = accountId,
- [COOKIE_TOKEN] = cookieToken,
- });
-
- return true;
- }
-
- cookie = null;
- return false;
- }
-
- ///
public bool TryGetValue(string key, [NotNullWhen(true)] out string? value)
{
return inner.TryGetValue(key, out value);
}
- ///
- /// 获取值
- ///
- /// 键
- /// 值或默认值
public string? GetValueOrDefault(string key)
{
return inner.GetValueOrDefault(key);
}
- ///
- /// 转换为Cookie的字符串表示
- ///
- /// Cookie的字符串表示
public override string ToString()
{
- return string.Join(';', inner.Select(kvp => $"{kvp.Key}={kvp.Value}"));
- }
-
- private bool TryGetSToken([NotNullWhen(true)] out Cookie? cookie)
- {
- if (TryGetValue(MID, out string? mid) && TryGetValue(STOKEN, out string? stoken) && TryGetValue(STUID, out string? stuid))
- {
- cookie = new Cookie(new()
- {
- [MID] = mid,
- [STOKEN] = stoken,
- [STUID] = stuid,
- });
-
- return true;
- }
-
- cookie = null;
- return false;
- }
-
- private bool TryGetLegacySToken([NotNullWhen(true)] out Cookie? cookie)
- {
- if (TryGetValue(STOKEN, out string? stoken) && TryGetValue(STUID, out string? stuid))
- {
- cookie = new Cookie(new()
- {
- [STOKEN] = stoken,
- [STUID] = stuid,
- });
-
- return true;
- }
-
- cookie = null;
- return false;
+ return inner.JoinToString(';', (builder, key, value) => builder.Append(key).Append('=').Append(value));
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieExtension.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieExtension.cs
new file mode 100644
index 00000000..1e37f57e
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/CookieExtension.cs
@@ -0,0 +1,68 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Hoyolab;
+
+[SuppressMessage("", "SA1310")]
+internal static class CookieExtension
+{
+ private const string LOGIN_TICKET = "login_ticket";
+ private const string LOGIN_UID = "login_uid";
+ private const string ACCOUNT_ID = "account_id";
+ private const string COOKIE_TOKEN = "cookie_token";
+ private const string LTOKEN = "ltoken";
+ private const string LTUID = "ltuid";
+ private const string MID = "mid";
+ private const string STOKEN = "stoken";
+ private const string STUID = "stuid";
+ private const string DEVICEFP = "DEVICEFP";
+
+ public static bool TryGetLoginTicket(this Cookie source, [NotNullWhen(true)] out Cookie? cookie)
+ {
+ return source.TryGetValuesToCookie([LOGIN_TICKET, LOGIN_UID], out cookie);
+ }
+
+ public static bool TryGetSToken(this Cookie source, bool isOversea, [NotNullWhen(true)] out Cookie? cookie)
+ {
+ return isOversea
+ ? source.TryGetValuesToCookie([STOKEN, STUID], out cookie)
+ : source.TryGetValuesToCookie([MID, STOKEN, STUID], out cookie);
+ }
+
+ public static bool TryGetLToken(this Cookie source, [NotNullWhen(true)] out Cookie? cookie)
+ {
+ return source.TryGetValuesToCookie([LTOKEN, LTUID], out cookie);
+ }
+
+ public static bool TryGetCookieToken(this Cookie source, [NotNullWhen(true)] out Cookie? cookie)
+ {
+ return source.TryGetValuesToCookie([ACCOUNT_ID, COOKIE_TOKEN], out cookie);
+ }
+
+ public static bool TryGetDeviceFp(this Cookie source, [NotNullWhen(true)] out string? deviceFp)
+ {
+ return source.TryGetValue(DEVICEFP, out deviceFp);
+ }
+
+ private static bool TryGetValuesToCookie(this Cookie source, in ReadOnlySpan keys, [NotNullWhen(true)] out Cookie? cookie)
+ {
+ Must.Range(keys.Length > 0, "Empty keys is not supported");
+ SortedDictionary cookieMap = [];
+
+ foreach (string key in keys)
+ {
+ if (source.TryGetValue(key, out string? value))
+ {
+ cookieMap.TryAdd(key, value);
+ }
+ else
+ {
+ cookie = default;
+ return false;
+ }
+ }
+
+ cookie = new(cookieMap);
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs
index be1ad870..e581f2a1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs
@@ -5,7 +5,6 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Web.Hoyolab.Annotation;
using Snap.Hutao.Web.Hoyolab.DataSigning;
-using Snap.Hutao.Web.Hoyolab.Hk4e.Sdk.Combo;
using Snap.Hutao.Web.Request.Builder;
using Snap.Hutao.Web.Request.Builder.Abstraction;
using Snap.Hutao.Web.Response;