diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/HttpClient/HttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/HttpClient/HttpClientConfiguration.cs index 76609c09..337bf1fd 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/HttpClient/HttpClientConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/Annotation/HttpClient/HttpClientConfiguration.cs @@ -25,7 +25,7 @@ internal enum HttpClientConfiguration XRpc2, /// - /// 国际服Hoyolab请求配置 + /// Hoyolab app /// XRpc3, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs index 9eca4792..c4fd585d 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/DependencyInjection/IocHttpClientConfiguration.cs @@ -68,10 +68,26 @@ internal static partial class IocHttpClientConfiguration /// /// 对于需要添加动态密钥的客户端使用此配置 - /// 国际服 API 测试 + /// Hoyolab app /// /// 配置后的客户端 private static void XRpc3Configuration(HttpClient client) + { + client.Timeout = Timeout.InfiniteTimeSpan; + client.DefaultRequestHeaders.UserAgent.ParseAdd(HoyolabOptions.UserAgentOversea); + client.DefaultRequestHeaders.Accept.ParseAdd(ApplicationJson); + client.DefaultRequestHeaders.Add("x-rpc-app_version", HoyolabOptions.XrpcVersionOversea); + client.DefaultRequestHeaders.Add("x-rpc-client_type", "5"); + client.DefaultRequestHeaders.Add("x-rpc-language", "zh-cn"); + client.DefaultRequestHeaders.Add("x-rpc-device_id", HoyolabOptions.DeviceId); + } + + /// + /// 对于需要添加动态密钥的客户端使用此配置 + /// Hoyolab web + /// + /// 配置后的客户端 + private static void XRpc4Configuration(HttpClient client) { client.Timeout = Timeout.InfiniteTimeSpan; client.DefaultRequestHeaders.UserAgent.ParseAdd(HoyolabOptions.UserAgentOversea); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs index 329972e1..2bf522e0 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/ApiOsEndpoints.cs @@ -87,7 +87,7 @@ internal static class ApiOsEndpoints #endregion - #region ApiOsTaKumiApi + #region ApiOsTakumiBindingApi /// /// 用户游戏角色 @@ -105,6 +105,12 @@ internal static class ApiOsEndpoints return $"{ApiAccountOsBindingApi}/getUserGameRolesByLtoken?game_biz=hk4e_global®ion={region}"; } + /// + /// Game Authkey + /// + public const string BindingGenAuthKey = $"{ApiAccountOsBindingApi}/genAuthKey"; + + #endregion #region BbsApiOsApi @@ -119,6 +125,11 @@ internal static class ApiOsEndpoints return $"{BbsApiOs}/community/painter/wapi/user/full"; } + /// + /// 用户详细信息 + /// + public const string UserFullInfo = $"{BbsApiOs}/community/user/wapi/getUserFullInfo?gid=2"; + /// /// 国际服角色基本信息 /// @@ -126,13 +137,13 @@ internal static class ApiOsEndpoints /// 角色基本信息字符串 public static string GameRecordRoleBasicInfo(in PlayerUid uid) { - return $"{BbsApiOsGameRecordApi}/roleBasicInfo?role_id={uid.Value}&server={uid.Region}"; + return $"{BbsApiOsGameRecordAppApi}/roleBasicInfo?role_id={uid.Value}&server={uid.Region}"; } /// /// 国际服角色信息 /// - public const string GameRecordCharacter = $"{BbsApiOsGameRecordApi}/character"; + public const string GameRecordCharacter = $"{BbsApiOsGameRecordAppApi}/character"; /// /// 国际服游戏记录实时便笺 @@ -141,7 +152,7 @@ internal static class ApiOsEndpoints /// 游戏记录实时便笺字符串 public static string GameRecordDailyNote(in PlayerUid uid) { - return $"{BbsApiOsGameRecordApi}/dailyNote?server={uid.Region}&role_id={uid.Value}"; + return $"{BbsApiOsGameRecordAppApi}/dailyNote?server={uid.Region}&role_id={uid.Value}"; } /// @@ -151,7 +162,7 @@ internal static class ApiOsEndpoints /// 游戏记录主页字符串 public static string GameRecordIndex(in PlayerUid uid) { - return $"{BbsApiOsGameRecordApi}/index?server={uid.Region}&role_id={uid.Value}"; + return $"{BbsApiOsGameRecordAppApi}/index?server={uid.Region}&role_id={uid.Value}"; } /// @@ -162,7 +173,7 @@ internal static class ApiOsEndpoints /// 深渊信息字符串 public static string GameRecordSpiralAbyss(Hoyolab.Takumi.GameRecord.SpiralAbyssSchedule scheduleType, in PlayerUid uid) { - return $"{BbsApiOsGameRecordApi}/spiralAbyss?schedule_type={(int)scheduleType}&role_id={uid.Value}&server={uid.Region}"; + return $"{BbsApiOsGameRecordAppApi}/spiralAbyss?server={uid.Region}&role_id={uid.Value}&schedule_type={(int)scheduleType}"; } #endregion @@ -247,12 +258,12 @@ internal static class ApiOsEndpoints private const string ApiOsTakumi = "https://api-os-takumi.hoyoverse.com"; private const string ApiOsTakumiBindingApi = $"{ApiOsTakumi}/binding/api"; - private const string ApiAccountOs = "https://api-account-os.hoyolab.com"; + private const string ApiAccountOs = "https://api-account-os.hoyoverse.com"; private const string ApiAccountOsBindingApi = $"{ApiAccountOs}/binding/api"; private const string ApiAccountOsAuthApi = $"{ApiAccountOs}/account/auth/api"; - private const string BbsApiOs = "https://bbs-api-os.hoyolab.com"; - private const string BbsApiOsGameRecordApi = $"{BbsApiOs}/game_record/genshin/api"; + private const string BbsApiOs = "https://bbs-api-os.hoyoverse.com"; + private const string BbsApiOsGameRecordAppApi = $"{BbsApiOs}/game_record/app/genshin/api"; private const string Hk4eApiOs = "https://hk4e-api-os.hoyoverse.com"; private const string Hk4eApiOsGachaInfoApi = $"{Hk4eApiOs}/event/gacha_info/api"; @@ -260,7 +271,7 @@ internal static class ApiOsEndpoints private const string SdkOsStatic = "https://sdk-os-static.mihoyo.com"; private const string SdkOsStaticLauncherApi = $"{SdkOsStatic}/hk4e_global/mdk/launcher/api"; - private const string SgPublicApi = "https://sg-public-api.hoyolab.com"; + private const string SgPublicApi = "https://sg-public-api.hoyoverse.com"; private const string WebApiOs = "https://webapi-os.account.hoyoverse.com"; private const string WebApiOsAccountApi = $"{WebApiOs}/Api"; @@ -275,5 +286,10 @@ internal static class ApiOsEndpoints /// public const string ActHoyolabReferer = "https://act.hoyolab.com/"; + /// + /// App hoyolab referer + /// + public const string AppHoyolabReferer = "https://app.hoyolab.com/"; + #endregion } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs index 366d0f12..1d0a768c 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs @@ -47,22 +47,4 @@ internal sealed partial class UserClient : IUserClient return Response.Response.DefaultIfNull(resp); } - - /// - /// 获取当前用户详细信息,使用 LToken - /// - /// 用户 - /// 取消令牌 - /// 详细信息 - [ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSK2)] - public async Task> GetOsUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default) - { - Response? resp = await httpClient - .SetUser(user, CookieType.LToken) - .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false) - .TryCatchGetFromJsonAsync>(ApiOsEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token) - .ConfigureAwait(false); - - return Response.Response.DefaultIfNull(resp); - } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs index ab1f0a8e..af6f2931 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/SaltType.cs @@ -14,6 +14,11 @@ internal enum SaltType /// None, + /// + /// PROD + /// + PROD, + /// /// X4 /// @@ -24,11 +29,6 @@ internal enum SaltType /// X6, - /// - /// PROD - /// - PROD, - /// /// K2 /// @@ -40,7 +40,22 @@ internal enum SaltType LK2, /// - /// Hoyolab K2 + /// OSK2 /// OSK2, + + /// + /// OSLK + /// + OSLK2, + + /// + /// OSX4 + /// + OSX4, + + /// + /// OSX6 + /// + OSX6, } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabOptions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabOptions.cs index 58847511..94073738 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabOptions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HoyolabOptions.cs @@ -41,19 +41,23 @@ internal sealed class HoyolabOptions : IOptions /// /// Hoyolab Rpc 版本 /// - public const string XrpcVersionOversea = "2.30.0"; + public const string XrpcVersionOversea = "2.31.0"; // https://github.com/UIGF-org/Hoyolab.Salt private static readonly ImmutableDictionary SaltsInner = new Dictionary() { + // Chinese [SaltType.K2] = "A4lPYtN0KGRVwE5M5Fm0DqQiC5VVMVM3", [SaltType.LK2] = "kkFiNdhyHqZ1VnDRHnU1podIvO4eiHcs", [SaltType.X4] = "xV8v4Qu54lUKrEYFZkJhB8cuOh9Asafs", [SaltType.X6] = "t0qEgfub6cvueAPgR5m9aQWWVciEer7v", [SaltType.PROD] = "JwYDpKvLj6MrMqqYU6jTKF17KNO2PXoS", - // This SALT is not reliable - [SaltType.OSK2] = "6cqshh5dhw73bzxn20oexa9k516chk7s", + // Oversea + [SaltType.OSK2] = "599uqkwc0dlqu3h6epzjzfhgyyrd44ae", + [SaltType.OSLK2] = "rk4xg2hakoi26nljpr099fv9fck1ah10", + [SaltType.OSX4] = "h4c1d6ywfq5bsbnbhm1bzq7bxzzv6srt", + [SaltType.OSX6] = "okr4obncj8bw5a65hbnn5oo6ixjc3l9w", }.ToImmutableDictionary(); private static string? deviceId; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClientOversea.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClientOversea.cs index 731174c8..1ca0e0d5 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClientOversea.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClientOversea.cs @@ -37,12 +37,12 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient /// 用户与角色 /// 取消令牌 /// 实时便笺 - [ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSK2)] + [ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSX4)] public async Task> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default) { Response? resp = await httpClient .SetUser(userAndUid.User, CookieType.Cookie) - .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false) .TryCatchGetFromJsonAsync>(ApiOsEndpoints.GameRecordDailyNote(userAndUid.Uid.Value), options, logger, token) .ConfigureAwait(false); @@ -55,12 +55,12 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient /// 用户与角色 /// 取消令牌 /// 玩家的基础信息 - [ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSK2)] + [ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSX4)] public async Task> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default) { Response? resp = await httpClient .SetUser(userAndUid.User, CookieType.Cookie) - .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false) .TryCatchGetFromJsonAsync>(ApiOsEndpoints.GameRecordIndex(userAndUid.Uid), options, logger, token) .ConfigureAwait(false); @@ -74,12 +74,12 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient /// 1:当期,2:上期 /// 取消令牌 /// 深渊信息 - [ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSK2)] + [ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSX4)] public async Task> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default) { Response? resp = await httpClient .SetUser(userAndUid.User, CookieType.Cookie) - .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false) .TryCatchGetFromJsonAsync>(ApiOsEndpoints.GameRecordSpiralAbyss(schedule, userAndUid.Uid), options, logger, token) .ConfigureAwait(false); @@ -93,14 +93,14 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient /// 玩家的基础信息 /// 取消令牌 /// 角色列表 - [ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSK2)] + [ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSX4)] public async Task> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default) { CharacterData data = new(userAndUid.Uid, playerInfo.Avatars.Select(x => x.Id)); Response? resp = await httpClient .SetUser(userAndUid.User, CookieType.Cookie) - .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false) .TryCatchPostAsJsonAsync>(ApiOsEndpoints.GameRecordCharacter, data, options, logger, token) .ConfigureAwait(false);