fix enka api

This commit is contained in:
DismissedLight
2023-02-07 10:21:36 +08:00
parent a1c0b4f830
commit 8a47ea8727
19 changed files with 193 additions and 177 deletions

View File

@@ -36,13 +36,13 @@ internal static class CoreEnvironment
/// 盐
/// </summary>
// https://github.com/UIGF-org/Hoyolab.Salt
public static readonly ImmutableDictionary<string, string> DynamicSecrets = new Dictionary<string, string>()
public static readonly ImmutableDictionary<SaltType, string> DynamicSecrets = new Dictionary<SaltType, string>()
{
[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();
/// <summary>

View File

@@ -2203,7 +2203,7 @@ namespace Snap.Hutao.Resource.Localization {
}
/// <summary>
/// 查找类似 移除用户 的本地化字符串。
/// 查找类似 移除角色 的本地化字符串。
/// </summary>
internal static string ViewPageDailyNoteRemoveToolTip {
get {

View File

@@ -832,7 +832,7 @@
<value>提醒通知</value>
</data>
<data name="ViewPageDailyNoteRemoveToolTip" xml:space="preserve">
<value>移除用户</value>
<value>移除角色</value>
</data>
<data name="ViewPageDailyNoteResinDiscountUsed" xml:space="preserve">
<value>本周已消耗减半次数</value>

View File

@@ -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<GachaItem> 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;

View File

@@ -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
{

View File

@@ -82,12 +82,11 @@ internal class GachaLogUrlWebCacheProvider : IGachaLogUrlProvider
ReadOnlySpan<byte> 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<byte> 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));
}

View File

@@ -18,7 +18,7 @@ public sealed partial class AdoptCalculatorDialog : ContentDialog
{
private readonly IServiceScope scope;
[SuppressMessage("", "IDE0052")]
private CalculatorJsInterface? dailyNoteJsInterface;
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µ<EFBFBD><C2B5><EFBFBD><EFBFBD>ɼ<EFBFBD><C9BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
@@ -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();
}
}

View File

@@ -19,7 +19,7 @@ public sealed partial class DailyNoteVerificationDialog : ContentDialog
private readonly IServiceScope scope;
private readonly UserAndUid userAndUid;
private DailyNoteJsInterface? dailyNoteJsInterface;
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// 构造一个新的实时便笺验证对话框
@@ -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();

View File

@@ -165,66 +165,65 @@
</ItemsControl.ItemContainerTransitions>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid
MinHeight="48"
Margin="0,8,0,0"
Padding="8"
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Background="{ThemeResource CardBackgroundBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="{ThemeResource CardBorderThickness}"
CornerRadius="{ThemeResource ControlCornerRadius}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<CheckBox
Grid.Column="0"
MinWidth="0"
MinHeight="0"
Margin="8,0"
Padding="0,0,0,0"
Command="{Binding Path=DataContext.SaveAchievementCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<Grid Grid.Column="1" Margin="8,0,0,0">
Style="{StaticResource BorderCardStyle}">
<Grid
MinHeight="48"
Margin="0,8,0,0"
Padding="8">
<Grid.ColumnDefinitions>
<!-- text -->
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<!-- time -->
<ColumnDefinition Width="auto"/>
<!-- pic -->
<ColumnDefinition Width="auto"/>
<!-- count -->
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="{Binding Inner.Title}"/>
<CheckBox
Grid.Column="0"
MinWidth="0"
MinHeight="0"
Margin="8,0"
Padding="0,0,0,0"
Command="{Binding Path=DataContext.SaveAchievementCommand, Source={StaticResource BindingProxy}}"
CommandParameter="{Binding}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
<Grid Grid.Column="1" Margin="8,0,0,0">
<Grid.ColumnDefinitions>
<!-- text -->
<ColumnDefinition/>
<!-- time -->
<ColumnDefinition Width="auto"/>
<!-- pic -->
<ColumnDefinition Width="auto"/>
<!-- count -->
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBlock Text="{Binding Inner.Title}"/>
<TextBlock
Margin="0,2,0,0"
Style="{StaticResource SecondaryTextStyle}"
Text="{Binding Inner.Description}"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
<TextBlock
Margin="0,2,0,0"
Style="{StaticResource SecondaryTextStyle}"
Text="{Binding Inner.Description}"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
<TextBlock
Grid.Column="1"
Margin="12,0,12,0"
VerticalAlignment="Center"
Text="{Binding Time}"
Visibility="{Binding IsChecked, Converter={StaticResource BoolToVisibilityConverter}}"/>
<Image
Grid.Column="2"
Height="32"
Source="ms-appx:///Resource/Icon/UI_ItemIcon_201.png"/>
<TextBlock
Grid.Column="3"
Margin="12,0,0,0"
VerticalAlignment="Center"
Text="{Binding Inner.FinishReward.Count}"/>
Grid.Column="1"
Margin="12,0,12,0"
VerticalAlignment="Center"
Text="{Binding Time}"
Visibility="{Binding IsChecked, Converter={StaticResource BoolToVisibilityConverter}}"/>
<Image
Grid.Column="2"
Height="32"
Source="ms-appx:///Resource/Icon/UI_ItemIcon_201.png"/>
<TextBlock
Grid.Column="3"
Margin="12,0,0,0"
VerticalAlignment="Center"
Text="{Binding Inner.FinishReward.Count}"/>
</Grid>
</Grid>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

View File

@@ -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<string>(SearchAchievement);
SortIncompletedSwitchCommand = new RelayCommand(UpdateAchievementsSort);
SaveAchievementCommand = new RelayCommand<Model.Binding.Achievement.Achievement>(SaveAchievement);
SaveAchievementCommand = new RelayCommand<BindingAchievement>(SaveAchievement);
}
/// <summary>
@@ -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<Model.Metadata.Achievement.Achievement> rawAchievements = await metadataService.GetAchievementsAsync(CancellationToken).ConfigureAwait(false);
List<Model.Binding.Achievement.Achievement> combined;
List<BindingAchievement> 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<int, GoalAggregation> counter = AchievementGoals.ToDictionary(x => x.Id, x => new GoalAggregation(x));
foreach (Model.Binding.Achievement.Achievement achievement in Achievements.SourceCollection.OfType<Model.Binding.Achievement.Achievement>())
foreach (BindingAchievement achievement in Achievements.SourceCollection.OfType<BindingAchievement>())
{
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)
{

View File

@@ -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;
/// <summary>
/// 养成计算器调用桥
/// </summary>
public class CalculatorJsInterface : MiHoYoJSInterface
{
/// <inheritdoc cref="MiHoYoJSInterface(CoreWebView2, IServiceProvider)"/>
public CalculatorJsInterface(CoreWebView2 webView, IServiceProvider serviceProvider)
: base(webView, serviceProvider)
{
}
}

View File

@@ -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;
/// <summary>
/// 实时便笺页面调用桥
/// </summary>
public class DailyNoteJsInterface : MiHoYoJSInterface
{
/// <summary>
/// 构造一个新的实时便笺页面调用桥
/// </summary>
/// <param name="webView">webview</param>
/// <param name="serviceProvider">服务提供器</param>
public DailyNoteJsInterface(CoreWebView2 webView, IServiceProvider serviceProvider)
: base(webView, serviceProvider)
{
}
}

View File

@@ -122,7 +122,7 @@ public class MiHoYoJSInterface
/// <returns>响应</returns>
public virtual JsResult<Dictionary<string, string>> 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
/// <returns>响应</returns>
public virtual JsResult<Dictionary<string, string>> GetDynamicSecrectV2(JsParam<DynamicSecrect2Playload> 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;

View File

@@ -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;

View File

@@ -47,8 +47,7 @@ internal class AccountClient
{
Response<GameAuthKey>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
// .SetReferer("https://app.mihoyo.com")
.SetReferer("https://app.mihoyo.com")
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, false)
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.AppAuthGenAuthKey, data, options, logger, token)
.ConfigureAwait(false);

View File

@@ -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;
/// <summary>
/// 动态密钥创建选项
/// </summary>
internal class DynamicSecretCreationOptions
{
private const string RandomRange = "abcdefghijklmnopqrstuvwxyz1234567890";
private readonly bool includeChars;
/// <summary>
/// 构造一个新的动态密钥创建选项
/// </summary>
/// <param name="option">选项字符串</param>
public DynamicSecretCreationOptions(string option)
{
string[] definations = option.Split('|');
string version = definations[0];
string saltType = definations[1];
string includeChars = definations[2];
Version = Enum.Parse<DynamicSecretVersion>(version);
SaltType = Enum.Parse<SaltType>(saltType);
this.includeChars = bool.Parse(includeChars);
}
/// <summary>
/// DS 版本
/// </summary>
public DynamicSecretVersion Version { get; }
/// <summary>
/// SALT 类型
/// </summary>
public SaltType SaltType { get; }
/// <summary>
/// 默认 Body
/// </summary>
public string DefaultBody
{
get => SaltType == SaltType.PROD ? "{}" : string.Empty;
}
/// <summary>
/// 随机字符串
/// </summary>
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)}";
}
}

View File

@@ -13,63 +13,43 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
[Injection(InjectAs.Transient)]
public class DynamicSecretHandler : DelegatingHandler
{
private const string RandomRange = "abcdefghijklmnopqrstuvwxyz1234567890";
/// <inheritdoc/>
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token)
{
if (request.Headers.TryGetValues("DS-Option", out IEnumerable<string>? 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)}";
}
}
}

View File

@@ -36,7 +36,7 @@ internal class GachaInfoClient
/// <param name="config">查询</param>
/// <param name="token">取消令牌</param>
/// <returns>单个祈愿记录页面</returns>
public async Task<Response<GachaLogPage>> GetGachaLogPageAsync(GachaLogConfigration config, CancellationToken token = default)
public async Task<Response<GachaLogPage>> GetGachaLogPageAsync(GachaLogQueryOptions config, CancellationToken token = default)
{
string query = config.AsQuery();

View File

@@ -9,7 +9,7 @@ namespace Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
/// <summary>
/// 祈愿记录请求配置
/// </summary>
public struct GachaLogConfigration
public struct GachaLogQueryOptions
{
/// <summary>
/// 尺寸
@@ -17,12 +17,12 @@ public struct GachaLogConfigration
public const int Size = 20;
/// <summary>
/// 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
/// <param name="query">原始查询字符串</param>
/// <param name="type">祈愿类型</param>
/// <param name="endId">终止Id</param>
public GachaLogConfigration(string query, GachaConfigType type, long endId = 0L)
public GachaLogQueryOptions(string query, GachaConfigType type, long endId = 0L)
{
innerQuery = QueryString.Parse(query);