From e925c5909cf03a4c906c207c6d796ca7d306b3b4 Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Tue, 27 Sep 2022 12:35:37 +0800 Subject: [PATCH] update readme --- README.md | 4 +- .../Snap.Hutao/Core/Caching/ImageCache.cs | 2 +- .../Snap.Hutao/Model/Binding/User.cs | 31 +++++-- .../Metadata/Converter/DescParamDescriptor.cs | 8 +- .../Snap.Hutao/Package.appxmanifest | 2 +- .../Snap.Hutao/Service/InfoBarService.cs | 14 +-- .../Snap.Hutao/Service/User/IUserService.cs | 11 ++- .../Snap.Hutao/Service/User/UserService.cs | 28 +++++- .../View/Dialog/UserAutoCookieDialog.xaml | 10 ++- .../View/Dialog/UserAutoCookieDialog.xaml.cs | 7 +- src/Snap.Hutao/Snap.Hutao/View/UserView.xaml | 5 +- .../Snap.Hutao/ViewModel/UserViewModel.cs | 86 ++++++++++++------- .../ViewModel/WikiAvatarViewModel.cs | 10 ++- 13 files changed, 147 insertions(+), 71 deletions(-) diff --git a/README.md b/README.md index 6a282c68..4aa6dd6e 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ ## 安装 * 前往 [下载页面](https://go.hut.ao/archive) 下载最新版本的 `胡桃` 安装包 -* 完全解压后,使用 powershell 运行 `install.ps1` 文件 +* (曾启用的可以跳过此步骤)在系统设置中打开 **开发者选项** 界面,勾选 `开发人员模式` 和 `允许 PowerShell 脚本` +* 完全解压后,右键使用 powershell 运行 `install.ps1` 文件 +* 安装完成后可以关闭 `允许 PowerShell 脚本` ## 特别感谢 diff --git a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs index f7f9fecd..56381866 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/Caching/ImageCache.cs @@ -15,7 +15,7 @@ namespace Snap.Hutao.Core.Caching; /// [Injection(InjectAs.Singleton, typeof(IImageCache))] [HttpClient(HttpClientConfigration.Default)] -[PrimaryHttpMessageHandler(MaxConnectionsPerServer = 20)] +[PrimaryHttpMessageHandler(MaxConnectionsPerServer = 16)] public class ImageCache : CacheBase, IImageCache { private const string DateAccessedProperty = "System.DateAccessed"; diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs index 9db27c14..2a51b012 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User.cs @@ -77,7 +77,7 @@ public class User : Observable /// /// cookie的字符串形式 /// 包含cookie信息的字典 - public static IDictionary ParseCookie(string cookie) + public static IDictionary MapCookie(string cookie) { SortedDictionary cookieDictionary = new(); @@ -142,9 +142,9 @@ public class User : Observable /// 验证客户端 /// 取消令牌 /// 是否升级成功 - internal async Task TryUpgradeAsync(IDictionary addition, AuthClient authClient, CancellationToken token) + internal async Task TryUpgradeByLoginTicketAsync(IDictionary addition, AuthClient authClient, CancellationToken token) { - IDictionary cookie = ParseCookie(Cookie!); + IDictionary cookie = MapCookie(Cookie!); if (addition.TryGetValue(CookieKeys.LOGIN_TICKET, out string? loginTicket)) { cookie[CookieKeys.LOGIN_TICKET] = loginTicket; @@ -155,7 +155,7 @@ public class User : Observable cookie[CookieKeys.LOGIN_UID] = loginUid; } - bool result = await TryAddStokenToCookieAsync(cookie, authClient, token).ConfigureAwait(false); + bool result = await TryRequestStokenAndAddToCookieAsync(cookie, authClient, token).ConfigureAwait(false); if (result) { @@ -165,12 +165,31 @@ public class User : Observable return result; } + /// + /// 添加 Stoken + /// + /// 额外的cookie + internal void AddStoken(IDictionary addition) + { + IDictionary cookie = MapCookie(Cookie!); + + if (addition.TryGetValue(CookieKeys.STOKEN, out string? stoken)) + { + cookie[CookieKeys.STOKEN] = stoken; + } + + if (addition.TryGetValue(CookieKeys.STUID, out string? stuid)) + { + cookie[CookieKeys.STUID] = stuid; + } + } + private static string ToCookieString(IDictionary cookie) { return string.Join(';', cookie.Select(kvp => $"{kvp.Key}={kvp.Value}")); } - private static async Task TryAddStokenToCookieAsync(IDictionary cookie, AuthClient authClient, CancellationToken token) + private static async Task TryRequestStokenAndAddToCookieAsync(IDictionary cookie, AuthClient authClient, CancellationToken token) { if (cookie.TryGetValue(CookieKeys.LOGIN_TICKET, out string? loginTicket)) { @@ -225,7 +244,7 @@ public class User : Observable return true; } - if (await TryAddStokenToCookieAsync(cookie, authClient, token).ConfigureAwait(false)) + if (await TryRequestStokenAndAddToCookieAsync(cookie, authClient, token).ConfigureAwait(false)) { Cookie = ToCookieString(cookie); } diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs index 1fbb6eff..7b2f41be 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/DescParamDescriptor.cs @@ -13,12 +13,10 @@ namespace Snap.Hutao.Model.Metadata.Converter; internal sealed class DescParamDescriptor : ValueConverterBase>> { /// - public override IList> Convert(DescParam rawDescParam) + public override IList> Convert(DescParam from) { - IList> parameters = rawDescParam.Parameters - .Select(param => new LevelParam( - param.Level.ToString(), - GetParameterInfos(rawDescParam, param.Parameters))) + IList> parameters = from.Parameters + .Select(param => new LevelParam(param.Level.ToString(), GetParameterInfos(from, param.Parameters))) .ToList(); return parameters; diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest index 29fa5e35..a4cf5ef8 100644 --- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest +++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest @@ -9,7 +9,7 @@ + Version="1.1.4.0" /> 胡桃 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs index 87ea680b..afed7251 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/InfoBarService.cs @@ -91,19 +91,18 @@ internal class InfoBarService : IInfoBarService PrepareInfoBarAndShow(InfoBarSeverity.Error, ex.GetType().Name, $"{title}\n{ex.Message}", delay); } - [SuppressMessage("", "VSTHRD100")] - private async void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay) + private void PrepareInfoBarAndShow(InfoBarSeverity severity, string? title, string? message, int delay) { if (infoBarStack is null) { return; } - await PrepareInfoBarAndShowInternalAsync(severity, title, message, delay).ConfigureAwait(false); + PrepareInfoBarAndShowInternalAsync(severity, title, message, delay).SafeForget(); } /// - /// 此方法应在主线程上运行 + /// 准备信息条并显示 /// /// 严重程度 /// 标题 @@ -122,11 +121,12 @@ internal class InfoBarService : IInfoBarService }; infoBar.Closed += OnInfoBarClosed; - Must.NotNull(infoBarStack!).Children.Add(infoBar); + infoBarStack!.Children.Add(infoBar); if (delay > 0) { - await Task.Delay(delay); + await Task.Delay(delay).ConfigureAwait(true); + infoBarStack.Children.Remove(infoBar); infoBar.IsOpen = false; } } @@ -136,7 +136,7 @@ internal class InfoBarService : IInfoBarService { await ThreadHelper.SwitchToMainThreadAsync(); - Must.NotNull(infoBarStack!).Children.Remove(sender); + infoBarStack!.Children.Remove(sender); sender.Closed -= OnInfoBarClosed; } } diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs index d94df729..9ada459a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/IUserService.cs @@ -38,10 +38,17 @@ public interface IUserService /// /// 尝试使用 login_ticket 升级用户 /// - /// 额外的Cookie + /// 额外的Cookie /// 取消令牌 /// 是否升级成功 - Task> TryUpgradeUserAsync(IDictionary addiition, CancellationToken token = default); + Task> TryUpgradeUserByLoginTicketAsync(IDictionary addiition, CancellationToken token = default); + + /// + /// 尝试使用 Stoken 升级用户 + /// + /// stoken + /// 是否升级成功 + Task> TryUpgradeUserByStokenAsync(IDictionary stoken); /// /// 异步移除用户 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index c77b7a18..b423a7b1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -118,6 +118,7 @@ internal class UserService : IUserService Verify.Operation(newUser.IsInitialized, "该用户尚未初始化"); // Sync cache + await ThreadHelper.SwitchToMainThreadAsync(); userCollection.Add(newUser); // Sync database @@ -180,7 +181,7 @@ internal class UserService : IUserService } /// - public async Task> TryUpgradeUserAsync(IDictionary addition, CancellationToken token = default) + public async Task> TryUpgradeUserByLoginTicketAsync(IDictionary addition, CancellationToken token = default) { Must.NotNull(userCollection!); if (addition.TryGetValue(CookieKeys.LOGIN_UID, out string? uid)) @@ -189,15 +190,36 @@ internal class UserService : IUserService if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is BindingUser userWithSameUid) { // Update user cookie here. - if (await userWithSameUid.TryUpgradeAsync(addition, authClient, token)) + if (await userWithSameUid.TryUpgradeByLoginTicketAsync(addition, authClient, token)) { appDbContext.Users.Update(userWithSameUid.Entity); await appDbContext.SaveChangesAsync().ConfigureAwait(false); - return new(true, uid); + return new(true, userWithSameUid.UserInfo?.Nickname ?? string.Empty); } } } return new(false, string.Empty); } + + /// + public async Task> TryUpgradeUserByStokenAsync(IDictionary stoken) + { + Must.NotNull(userCollection!); + if (stoken.TryGetValue(CookieKeys.STUID, out string? uid)) + { + // 查找是否有相同的uid + if (userCollection.SingleOrDefault(u => u.UserInfo!.Uid == uid) is BindingUser userWithSameUid) + { + // Update user cookie here. + userWithSameUid.AddStoken(stoken); + + appDbContext.Users.Update(userWithSameUid.Entity); + await appDbContext.SaveChangesAsync().ConfigureAwait(false); + return new(true, userWithSameUid.UserInfo?.Nickname ?? string.Empty); + } + } + + return new(false, string.Empty); + } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml index f3f54833..63402673 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml @@ -19,12 +19,14 @@ + - + diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml.cs index 9cb2e1e4..947da7e4 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/UserAutoCookieDialog.xaml.cs @@ -13,7 +13,7 @@ namespace Snap.Hutao.View.Dialog; /// public sealed partial class UserAutoCookieDialog : ContentDialog { - private IDictionary? cookieString; + private IDictionary? cookie; /// /// 构造一个新的用户自动Cookie对话框 @@ -32,7 +32,7 @@ public sealed partial class UserAutoCookieDialog : ContentDialog public async Task>> GetInputCookieAsync() { ContentDialogResult result = await ShowAsync(); - return new(result == ContentDialogResult.Primary && cookieString != null, cookieString!); + return new(result == ContentDialogResult.Primary && cookie != null, cookie!); } [SuppressMessage("", "VSTHRD100")] @@ -62,8 +62,7 @@ public sealed partial class UserAutoCookieDialog : ContentDialog { CoreWebView2CookieManager manager = WebView.CoreWebView2.CookieManager; IReadOnlyList cookies = await manager.GetCookiesAsync("https://user.mihoyo.com"); - cookieString = cookies.ToDictionary(c => c.Name, c => c.Value); - + cookie = cookies.ToDictionary(c => c.Name, c => c.Value); WebView.CoreWebView2.SourceChanged -= OnCoreWebView2SourceChanged; } catch (Exception) diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index 99656533..3f6f2da3 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -9,6 +9,7 @@ xmlns:mxi="using:Microsoft.Xaml.Interactivity" xmlns:shc="using:Snap.Hutao.Control" xmlns:shvm="using:Snap.Hutao.ViewModel" + xmlns:shcm="using:Snap.Hutao.Control.Markup" mc:Ignorable="d" d:DataContext="{d:DesignInstance shvm:UserViewModel}"> @@ -199,11 +200,11 @@ Text="Cookie"/> diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs index 35b29883..df2e2851 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs @@ -10,7 +10,6 @@ using Snap.Hutao.Service.Abstraction; using Snap.Hutao.View.Dialog; using Snap.Hutao.Web.Hoyolab; using System.Collections.ObjectModel; -using System.Net; using Windows.ApplicationModel.DataTransfer; namespace Snap.Hutao.ViewModel; @@ -21,8 +20,6 @@ namespace Snap.Hutao.ViewModel; [Injection(InjectAs.Transient)] internal class UserViewModel : ObservableObject { - private const string AccountIdKey = "account_id"; - private readonly IUserService userService; private readonly IInfoBarService infoBarService; @@ -42,7 +39,7 @@ internal class UserViewModel : ObservableObject OpenUICommand = asyncRelayCommandFactory.Create(OpenUIAsync); AddUserCommand = asyncRelayCommandFactory.Create(AddUserAsync); - UpgradeToStokenCommand = asyncRelayCommandFactory.Create(UpgradeToStokenAsync); + UpgradeToStokenCommand = asyncRelayCommandFactory.Create(UpgradeByLoginTicketAsync); RemoveUserCommand = asyncRelayCommandFactory.Create(RemoveUserAsync); CopyCookieCommand = new RelayCommand(CopyCookie); } @@ -92,35 +89,45 @@ internal class UserViewModel : ObservableObject /// public ICommand CopyCookieCommand { get; } - private static bool TryValidateCookie(IDictionary map, [NotNullWhen(true)] out IDictionary? filteredCookie) + private static (bool Valid, bool Upgrade) TryValidateCookie(IDictionary map, out IDictionary cookie) { int validFlag = 4; + int stokenFlag = 2; - SortedDictionary filter = new(); + cookie = new SortedDictionary(); foreach ((string key, string value) in map) { - if (key == CookieKeys.COOKIE_TOKEN || key == CookieKeys.ACCOUNT_ID || key == CookieKeys.LTOKEN || key == CookieKeys.LTUID) + switch (key) { - validFlag--; - filter.Add(key, value); - } - else if (key == CookieKeys.STOKEN || key == CookieKeys.STUID || key == CookieKeys.LOGIN_TICKET || key == CookieKeys.LOGIN_UID) - { - filter.Add(key, value); + case CookieKeys.COOKIE_TOKEN: + case CookieKeys.ACCOUNT_ID: + case CookieKeys.LTOKEN: + case CookieKeys.LTUID: + { + validFlag--; + cookie.Add(key, value); + break; + } + + case CookieKeys.STOKEN: + case CookieKeys.STUID: + { + stokenFlag--; + cookie.Add(key, value); + break; + } + + case CookieKeys.LOGIN_TICKET: + case CookieKeys.LOGIN_UID: + { + cookie.Add(key, value); + break; + } } } - if (validFlag == 0) - { - filteredCookie = filter; - return true; - } - else - { - filteredCookie = null; - return false; - } + return (validFlag == 0, stokenFlag == 0); } private async Task OpenUIAsync() @@ -138,11 +145,12 @@ internal class UserViewModel : ObservableObject // User confirms the input if (result.IsOk) { - if (TryValidateCookie(User.ParseCookie(result.Value), out IDictionary? filteredCookie)) + (bool valid, bool upgradable) = TryValidateCookie(User.MapCookie(result.Value), out IDictionary cookie); + if (valid) { - if (await userService.CreateUserAsync(filteredCookie).ConfigureAwait(false) is User user) + if (await userService.CreateUserAsync(cookie).ConfigureAwait(false) is User user) { - switch (await userService.TryAddUserAsync(user, filteredCookie[AccountIdKey]).ConfigureAwait(false)) + switch (await userService.TryAddUserAsync(user, cookie[CookieKeys.ACCOUNT_ID]).ConfigureAwait(false)) { case UserAddResult.Added: infoBarService.Success($"用户 [{user.UserInfo!.Nickname}] 添加成功"); @@ -164,12 +172,28 @@ internal class UserViewModel : ObservableObject } else { - infoBarService.Warning("提供的文本不是正确的 Cookie ,请重新输入"); + if (upgradable) + { + (bool success, string nickname) = await userService.TryUpgradeUserByStokenAsync(cookie).ConfigureAwait(false); + + if (success) + { + infoBarService.Information($"用户 [{nickname}] 的 Stoken 更新成功"); + } + else + { + infoBarService.Warning($"未找到匹配的可升级用户"); + } + } + else + { + infoBarService.Warning("提供的文本不是正确的 Cookie ,请重新输入"); + } } } } - private async Task UpgradeToStokenAsync() + private async Task UpgradeByLoginTicketAsync() { // Get cookie from user input MainWindow mainWindow = Ioc.Default.GetRequiredService(); @@ -178,10 +202,10 @@ internal class UserViewModel : ObservableObject // User confirms the input if (isOk) { - (bool isUpgradeSucceed, string uid) = await userService.TryUpgradeUserAsync(addition).ConfigureAwait(false); - if (isUpgradeSucceed) + (bool isUpgraded, string nickname) = await userService.TryUpgradeUserByLoginTicketAsync(addition).ConfigureAwait(false); + if (isUpgraded) { - infoBarService.Information($"用户 [{uid}] 的 Cookie 已成功添加 Stoken"); + infoBarService.Information($"用户 [{nickname}] 的 Cookie 已成功添加 Stoken"); } else { diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs index 86230ad9..bf26472d 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs @@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.WinUI.UI; +using Snap.Hutao.Core.Threading; using Snap.Hutao.Factory.Abstraction; using Snap.Hutao.Model; using Snap.Hutao.Model.Intrinsic; @@ -143,13 +144,14 @@ internal class WikiAvatarViewModel : ObservableObject private async Task OpenUIAsync() { - if (await metadataService.InitializeAsync()) + if (await metadataService.InitializeAsync().ConfigureAwait(false)) { - IList avatars = await metadataService.GetAvatarsAsync(); + IList avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false); IOrderedEnumerable sorted = avatars .OrderBy(avatar => avatar.BeginTime) .ThenBy(avatar => avatar.Sort); + await ThreadHelper.SwitchToMainThreadAsync(); Avatars = new AdvancedCollectionView(sorted.ToList(), true); Avatars.MoveCurrentToFirst(); } @@ -174,7 +176,7 @@ internal class WikiAvatarViewModel : ObservableObject .Select(e => e.Value.Value) .ToList(); - List targeQualities = FilterQualityInfos + List targetQualities = FilterQualityInfos .Where(e => e.IsSelected) .Select(e => e.Value.Value) .ToList(); @@ -188,7 +190,7 @@ internal class WikiAvatarViewModel : ObservableObject && targetElements.Contains(avatar.FetterInfo.VisionBefore) && targetAssociations.Contains(avatar.FetterInfo.Association) && targetWeaponTypes.Contains(avatar.Weapon) - && targeQualities.Contains(avatar.Quality) + && targetQualities.Contains(avatar.Quality) && targetBodies.Contains(avatar.Body); if (!Avatars.Contains(Selected))