From 8a47ea8727c2dab93879638ae9d08ac447e8370b Mon Sep 17 00:00:00 2001 From: DismissedLight <1686188646@qq.com> Date: Tue, 7 Feb 2023 10:21:36 +0800 Subject: [PATCH] fix enka api --- .../Snap.Hutao/Core/CoreEnvironment.cs | 12 +- .../Resource/Localization/SH.Designer.cs | 2 +- .../Snap.Hutao/Resource/Localization/SH.resx | 2 +- .../Service/GachaLog/GachaLogService.cs | 4 +- .../UrlProvider/GachaLogUrlStokenProvider.cs | 2 +- .../GachaLogUrlWebCacheProvider.cs | 3 +- .../View/Dialog/AdoptCalculatorDialog.xaml.cs | 6 +- .../DailyNoteVerificationDialog.xaml.cs | 12 +- .../Snap.Hutao/View/Page/AchievementPage.xaml | 105 +++++++++--------- .../ViewModel/AchievementViewModel.cs | 19 ++-- .../Web/Bridge/CalculatorJsInterface.cs | 18 --- .../Web/Bridge/DailyNoteJsInterface.cs | 22 ---- .../Web/Bridge/MiHoYoJSInterface.cs | 4 +- .../Snap.Hutao/Web/Enka/EnkaClient.cs | 2 +- .../Web/Hoyolab/App/Account/AccountClient.cs | 3 +- .../DynamicSecretCreationOptions.cs | 78 +++++++++++++ .../DynamicSecret/DynamicSecretHandler.cs | 66 ++++------- .../Hk4e/Event/GachaInfo/GachaInfoClient.cs | 2 +- ...onfigration.cs => GachaLogQueryOptions.cs} | 8 +- 19 files changed, 193 insertions(+), 177 deletions(-) delete mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Bridge/CalculatorJsInterface.cs delete mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Bridge/DailyNoteJsInterface.cs create mode 100644 src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretCreationOptions.cs rename src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/{GachaLogConfigration.cs => GachaLogQueryOptions.cs} (93%) diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs index fb5f90cc..278df815 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs @@ -36,13 +36,13 @@ internal static class CoreEnvironment /// 盐 /// // https://github.com/UIGF-org/Hoyolab.Salt - public static readonly ImmutableDictionary DynamicSecrets = new Dictionary() + public static readonly ImmutableDictionary DynamicSecrets = new Dictionary() { - [nameof(SaltType.K2)] = "dZAwGk4e9aC0MXXItkwnHamjA1x30IYw", - [nameof(SaltType.LK2)] = "IEIZiKYaput2OCKQprNuGsog1NZc1FkS", - [nameof(SaltType.X4)] = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs", - [nameof(SaltType.X6)] = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v", - [nameof(SaltType.PROD)] = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS", + [SaltType.K2] = "dZAwGk4e9aC0MXXItkwnHamjA1x30IYw", + [SaltType.LK2] = "IEIZiKYaput2OCKQprNuGsog1NZc1FkS", + [SaltType.X4] = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs", + [SaltType.X6] = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v", + [SaltType.PROD] = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS", }.ToImmutableDictionary(); /// diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs index aaec7368..8298040f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.Designer.cs @@ -2203,7 +2203,7 @@ namespace Snap.Hutao.Resource.Localization { } /// - /// 查找类似 移除用户 的本地化字符串。 + /// 查找类似 移除角色 的本地化字符串。 /// internal static string ViewPageDailyNoteRemoveToolTip { get { diff --git a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx index 942d9e02..86446d06 100644 --- a/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx +++ b/src/Snap.Hutao/Snap.Hutao/Resource/Localization/SH.resx @@ -832,7 +832,7 @@ 提醒通知 - 移除用户 + 移除角色 本周已消耗减半次数 diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs index 951508d4..357252dd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/GachaLogService.cs @@ -250,7 +250,7 @@ internal class GachaLogService : IGachaLogService { state.ConfigType = configType; long? dbEndId = null; - GachaLogConfigration configration = new(query, configType); + GachaLogQueryOptions configration = new(query, configType); List itemsToAdd = new(); do @@ -285,7 +285,7 @@ internal class GachaLogService : IGachaLogService progress.Report(state); - if (currentTypeAddingCompleted || items.Count < GachaLogConfigration.Size) + if (currentTypeAddingCompleted || items.Count < GachaLogQueryOptions.Size) { // exit current type fetch loop break; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs index 11b1a33e..231674a3 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs @@ -42,7 +42,7 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider if (authkeyResponse.IsOk()) { - return new(true, GachaLogConfigration.AsQuery(data, authkeyResponse.Data)); + return new(true, GachaLogQueryOptions.AsQuery(data, authkeyResponse.Data)); } else { diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs index 11239381..d1a8bdee 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlWebCacheProvider.cs @@ -82,12 +82,11 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider ReadOnlySpan match = isOversea ? "https://webstatic-sea.hoyoverse.com/genshin/event/e20190909gacha-v2/index.html"u8 : "https://webstatic.mihoyo.com/hk4e/event/e20190909gacha-v2/index.html"u8; - ReadOnlySpan zero = "\0"u8; int index = span.LastIndexOf(match); if (index >= 0) { - int length = span[index..].IndexOf(zero); + int length = span[index..].IndexOf("\0"u8); return Encoding.UTF8.GetString(span.Slice(index, length)); } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/AdoptCalculatorDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/AdoptCalculatorDialog.xaml.cs index 2ac99190..6df4bd74 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/AdoptCalculatorDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/AdoptCalculatorDialog.xaml.cs @@ -18,7 +18,7 @@ public sealed partial class AdoptCalculatorDialog : ContentDialog { private readonly IServiceScope scope; [SuppressMessage("", "IDE0052")] - private CalculatorJsInterface? dailyNoteJsInterface; + private MiHoYoJSInterface? jsInterface; /// /// һµɼԻ @@ -50,7 +50,7 @@ public sealed partial class AdoptCalculatorDialog : ContentDialog } coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent(); - dailyNoteJsInterface = new(coreWebView2, scope.ServiceProvider); + jsInterface = new(coreWebView2, scope.ServiceProvider); #if DEBUG coreWebView2.OpenDevToolsWindow(); @@ -60,7 +60,7 @@ public sealed partial class AdoptCalculatorDialog : ContentDialog private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) { - dailyNoteJsInterface = null; + jsInterface = null; scope.Dispose(); } } diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs index 95a3f61e..cdf2e964 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs +++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/DailyNoteVerificationDialog.xaml.cs @@ -19,7 +19,7 @@ public sealed partial class DailyNoteVerificationDialog : ContentDialog private readonly IServiceScope scope; private readonly UserAndUid userAndUid; - private DailyNoteJsInterface? dailyNoteJsInterface; + private MiHoYoJSInterface? jsInterface; /// /// 构造一个新的实时便笺验证对话框 @@ -45,8 +45,8 @@ public sealed partial class DailyNoteVerificationDialog : ContentDialog Model.Entity.User user = userAndUid.User; coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent(); - dailyNoteJsInterface = new(coreWebView2, scope.ServiceProvider); - dailyNoteJsInterface.ClosePageRequested += OnClosePageRequested; + jsInterface = new(coreWebView2, scope.ServiceProvider); + jsInterface.ClosePageRequested += OnClosePageRequested; string query = $"?role_id={userAndUid.Uid.Value}&server={userAndUid.Uid.Region}"; coreWebView2.Navigate($"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/{query}"); @@ -59,10 +59,10 @@ public sealed partial class DailyNoteVerificationDialog : ContentDialog private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args) { - if (dailyNoteJsInterface != null) + if (jsInterface != null) { - dailyNoteJsInterface!.ClosePageRequested -= OnClosePageRequested; - dailyNoteJsInterface = null; + jsInterface!.ClosePageRequested -= OnClosePageRequested; + jsInterface = null; } scope.Dispose(); diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml index d34e6d8c..02da1683 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AchievementPage.xaml @@ -165,66 +165,65 @@ - - - - - - - - + Style="{StaticResource BorderCardStyle}"> + - + - - - - - - - - + + + + + + + + + + + + + + + + + - - - - + Grid.Column="1" + Margin="12,0,12,0" + VerticalAlignment="Center" + Text="{Binding Time}" + Visibility="{Binding IsChecked, Converter={StaticResource BoolToVisibilityConverter}}"/> + + + - + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs index 842c258b..a40fda32 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/AchievementViewModel.cs @@ -20,6 +20,7 @@ using Snap.Hutao.View.Dialog; using System.Collections.ObjectModel; using System.Runtime.InteropServices; using Windows.Storage.Pickers; +using BindingAchievement = Snap.Hutao.Model.Binding.Achievement.Achievement; using BindingAchievementGoal = Snap.Hutao.Model.Binding.Achievement.AchievementGoal; using EntityAchievementArchive = Snap.Hutao.Model.Entity.AchievementArchive; using MetadataAchievementGoal = Snap.Hutao.Model.Metadata.Achievement.AchievementGoal; @@ -33,8 +34,8 @@ namespace Snap.Hutao.ViewModel; [SuppressMessage("", "SA1124")] internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipient { - private static readonly SortDescription IncompletedItemsFirstSortDescription = new(nameof(Model.Binding.Achievement.Achievement.IsChecked), SortDirection.Ascending); - private static readonly SortDescription CompletionTimeSortDescription = new(nameof(Model.Binding.Achievement.Achievement.Time), SortDirection.Descending); + private static readonly SortDescription IncompletedItemsFirstSortDescription = new(nameof(BindingAchievement.IsChecked), SortDirection.Ascending); + private static readonly SortDescription CompletionTimeSortDescription = new(nameof(BindingAchievement.Time), SortDirection.Descending); private readonly IAchievementService achievementService; private readonly IMetadataService metadataService; @@ -84,7 +85,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien RemoveArchiveCommand = new AsyncRelayCommand(RemoveArchiveAsync); SearchAchievementCommand = new RelayCommand(SearchAchievement); SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort); - SaveAchievementCommand = new RelayCommand(SaveAchievement); + SaveAchievementCommand = new RelayCommand(SaveAchievement); } /// @@ -352,13 +353,13 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien { if (search.Length == 5 && int.TryParse(search, out int achiId)) { - Achievements.Filter = (object o) => ((Model.Binding.Achievement.Achievement)o).Inner.Id == achiId; + Achievements.Filter = (object o) => ((BindingAchievement)o).Inner.Id == achiId; } else { Achievements.Filter = (object o) => { - Model.Binding.Achievement.Achievement achi = (Model.Binding.Achievement.Achievement)o; + BindingAchievement achi = (BindingAchievement)o; return achi.Inner.Title.Contains(search) || achi.Inner.Description.Contains(search); }; } @@ -494,7 +495,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien private async Task UpdateAchievementsAsync(EntityAchievementArchive archive) { List rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false); - List combined; + List combined; try { combined = achievementService.GetAchievements(archive, rawAchievements); @@ -535,7 +536,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien if (Achievements != null) { Achievements.Filter = goal != null - ? ((object o) => o is Model.Binding.Achievement.Achievement achi && achi.Inner.Goal == goal.Id) + ? ((object o) => o is BindingAchievement achi && achi.Inner.Goal == goal.Id) : null; } } @@ -547,7 +548,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien if (Achievements != null && AchievementGoals != null) { Dictionary counter = AchievementGoals.ToDictionary(x => x.Id, x => new GoalAggregation(x)); - foreach (Model.Binding.Achievement.Achievement achievement in Achievements.SourceCollection.OfType()) + foreach (BindingAchievement achievement in Achievements.SourceCollection.OfType()) { ref GoalAggregation aggregation = ref CollectionsMarshal.GetValueRefOrNullRef(counter, achievement.Inner.Goal); aggregation.Count += 1; @@ -568,7 +569,7 @@ internal class AchievementViewModel : Abstraction.ViewModel, INavigationRecipien } } - private void SaveAchievement(Model.Binding.Achievement.Achievement? achievement) + private void SaveAchievement(BindingAchievement? achievement) { if (achievement != null) { diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CalculatorJsInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CalculatorJsInterface.cs deleted file mode 100644 index 9361f4f9..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/CalculatorJsInterface.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.Web.WebView2.Core; - -namespace Snap.Hutao.Web.Bridge; - -/// -/// 养成计算器调用桥 -/// -public class CalculatorJsInterface : MiHoYoJSInterface -{ - /// - public CalculatorJsInterface(CoreWebView2 webView, IServiceProvider serviceProvider) - : base(webView, serviceProvider) - { - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/DailyNoteJsInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/DailyNoteJsInterface.cs deleted file mode 100644 index 7a3c1369..00000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/DailyNoteJsInterface.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.Web.WebView2.Core; - -namespace Snap.Hutao.Web.Bridge; - -/// -/// 实时便笺页面调用桥 -/// -public class DailyNoteJsInterface : MiHoYoJSInterface -{ - /// - /// 构造一个新的实时便笺页面调用桥 - /// - /// webview - /// 服务提供器 - public DailyNoteJsInterface(CoreWebView2 webView, IServiceProvider serviceProvider) - : base(webView, serviceProvider) - { - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs index a7585160..f3c5c70c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs @@ -122,7 +122,7 @@ public class MiHoYoJSInterface /// 响应 public virtual JsResult> GetDynamicSecrectV1(JsParam param) { - string salt = Core.CoreEnvironment.DynamicSecrets[nameof(SaltType.LK2)]; + string salt = Core.CoreEnvironment.DynamicSecrets[SaltType.LK2]; long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); string r = GetRandomString(); string check = Core.Convert.ToMd5HexString($"salt={salt}&t={t}&r={r}").ToLowerInvariant(); @@ -152,7 +152,7 @@ public class MiHoYoJSInterface /// 响应 public virtual JsResult> GetDynamicSecrectV2(JsParam param) { - string salt = Core.CoreEnvironment.DynamicSecrets[nameof(SaltType.X4)]; + string salt = Core.CoreEnvironment.DynamicSecrets[SaltType.X4]; long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); int r = GetRandom(); string b = param.Payload.Body; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs index 380c0d21..5d2df57e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Enka/EnkaClient.cs @@ -14,7 +14,7 @@ namespace Snap.Hutao.Web.Enka; [HttpClient(HttpClientConfigration.Default)] internal class EnkaClient { - private const string EnkaAPI = "https://enka.network/u/{0}/__data.json"; + private const string EnkaAPI = "https://enka.network/api/uid/{0}"; private const string EnkaAPIHutaoForward = "https://enka-api.hut.ao/{0}"; private readonly HttpClient httpClient; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs index 947486d7..c1441737 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/App/Account/AccountClient.cs @@ -47,8 +47,7 @@ internal class AccountClient { Response? resp = await httpClient .SetUser(user, CookieType.Stoken) - - // .SetReferer("https://app.mihoyo.com") + .SetReferer("https://app.mihoyo.com") .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, false) .TryCatchPostAsJsonAsync>(ApiEndpoints.AppAuthGenAuthKey, data, options, logger, token) .ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretCreationOptions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretCreationOptions.cs new file mode 100644 index 00000000..a2095b26 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretCreationOptions.cs @@ -0,0 +1,78 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +using System.Text; + +namespace Snap.Hutao.Web.Hoyolab.DynamicSecret; + +/// +/// 动态密钥创建选项 +/// +internal class DynamicSecretCreationOptions +{ + private const string RandomRange = "abcdefghijklmnopqrstuvwxyz1234567890"; + + private readonly bool includeChars; + + /// + /// 构造一个新的动态密钥创建选项 + /// + /// 选项字符串 + public DynamicSecretCreationOptions(string option) + { + string[] definations = option.Split('|'); + string version = definations[0]; + string saltType = definations[1]; + string includeChars = definations[2]; + + Version = Enum.Parse(version); + SaltType = Enum.Parse(saltType); + this.includeChars = bool.Parse(includeChars); + } + + /// + /// DS 版本 + /// + public DynamicSecretVersion Version { get; } + + /// + /// SALT 类型 + /// + public SaltType SaltType { get; } + + /// + /// 默认 Body + /// + public string DefaultBody + { + get => SaltType == SaltType.PROD ? "{}" : string.Empty; + + } + + /// + /// 随机字符串 + /// + public string RandomString + { + get => includeChars ? GetRandomStringWithChars() : GetRandomStringNoChars(); + } + + private static string GetRandomStringWithChars() + { + StringBuilder sb = new(6); + + for (int i = 0; i < 6; i++) + { + int pos = Random.Shared.Next(0, RandomRange.Length); + sb.Append(RandomRange[pos]); + } + + return sb.ToString(); + } + + private static string GetRandomStringNoChars() + { + int rand = Random.Shared.Next(100000, 200000); + return $"{(rand == 100000 ? 642367 : rand)}"; + } +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretHandler.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretHandler.cs index 65007186..5351d6a1 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretHandler.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretHandler.cs @@ -13,63 +13,43 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret; [Injection(InjectAs.Transient)] public class DynamicSecretHandler : DelegatingHandler { - private const string RandomRange = "abcdefghijklmnopqrstuvwxyz1234567890"; - /// protected override async Task SendAsync(HttpRequestMessage request, CancellationToken token) { if (request.Headers.TryGetValues("DS-Option", out IEnumerable? values)) { - string[] definations = values.Single().Split('|'); - string version = definations[0]; - string saltType = definations[1]; - bool includeChars = definations[2] == "true"; - - string salt = Core.CoreEnvironment.DynamicSecrets[saltType]; - - long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - - string r = includeChars ? GetRandomStringWithChars() : GetRandomStringNoChars(); - - string dsContent = $"salt={salt}&t={t}&r={r}"; - - if (version == nameof(DynamicSecretVersion.Gen2)) - { - string b = request.Content != null - ? await request.Content.ReadAsStringAsync(token).ConfigureAwait(false) - : (saltType == nameof(SaltType.PROD) ? "{}" : string.Empty); // PROD's default value is {} - - string[] queries = Uri.UnescapeDataString(request.RequestUri!.Query).Split('?', 2); - string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty; - - dsContent = $"{dsContent}&b={b}&q={q}"; - } - - string check = Core.Convert.ToMd5HexString(dsContent).ToLowerInvariant(); - + DynamicSecretCreationOptions options = new(values.Single()); + await ProcessRequestWithOptionsAsync(request, options, token).ConfigureAwait(false); request.Headers.Remove("DS-Option"); - request.Headers.Set("DS", $"{t},{r},{check}"); } return await base.SendAsync(request, token).ConfigureAwait(false); } - private static string GetRandomStringWithChars() + private static async Task ProcessRequestWithOptionsAsync(HttpRequestMessage request, DynamicSecretCreationOptions options, CancellationToken token) { - StringBuilder sb = new(6); + string salt = Core.CoreEnvironment.DynamicSecrets[options.SaltType]; - for (int i = 0; i < 6; i++) + long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + + string r = options.RandomString; + + string dsContent = $"salt={salt}&t={t}&r={r}"; + + // ds2 b & q process + if (options.Version == DynamicSecretVersion.Gen2) { - int pos = Random.Shared.Next(0, RandomRange.Length); - sb.Append(RandomRange[pos]); + string b = request.Content != null + ? await request.Content.ReadAsStringAsync(token).ConfigureAwait(false) + : options.DefaultBody; // PROD's default value is {} + + string[] queries = Uri.UnescapeDataString(request.RequestUri!.Query).Split('?', 2); + string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty; + + dsContent = $"{dsContent}&b={b}&q={q}"; } - return sb.ToString(); + string check = Core.Convert.ToMd5HexString(dsContent).ToLowerInvariant(); + request.Headers.Set("DS", $"{t},{r},{check}"); } - - private static string GetRandomStringNoChars() - { - int rand = Random.Shared.Next(100000, 200000); - return $"{(rand == 100000 ? 642367 : rand)}"; - } -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs index 80516df3..e3aa3858 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaInfoClient.cs @@ -36,7 +36,7 @@ internal class GachaInfoClient /// 查询 /// 取消令牌 /// 单个祈愿记录页面 - public async Task> GetGachaLogPageAsync(GachaLogConfigration config, CancellationToken token = default) + public async Task> GetGachaLogPageAsync(GachaLogQueryOptions config, CancellationToken token = default) { string query = config.AsQuery(); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogConfigration.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs similarity index 93% rename from src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogConfigration.cs rename to src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs index 015d80cf..65cb4a3d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogConfigration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Hk4e/Event/GachaInfo/GachaLogQueryOptions.cs @@ -9,7 +9,7 @@ namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo; /// /// 祈愿记录请求配置 /// -public struct GachaLogConfigration +public struct GachaLogQueryOptions { /// /// 尺寸 @@ -17,12 +17,12 @@ public struct GachaLogConfigration public const int Size = 20; /// - /// Below keys are required: + /// Keys required: /// authkey_ver /// auth_appid /// authkey /// sign_type - /// Below keys used as control: + /// Keys used as control: /// lang /// gacha_type /// size @@ -36,7 +36,7 @@ public struct GachaLogConfigration /// 原始查询字符串 /// 祈愿类型 /// 终止Id - public GachaLogConfigration(string query, GachaConfigType type, long endId = 0L) + public GachaLogQueryOptions(string query, GachaConfigType type, long endId = 0L) { innerQuery = QueryString.Parse(query);