Merge pull request #723 from Xhichn/Dev

Improve HoYoLAB API request
This commit is contained in:
DismissedLight
2023-05-16 16:21:39 +08:00
committed by GitHub
7 changed files with 80 additions and 47 deletions

View File

@@ -25,7 +25,7 @@ internal enum HttpClientConfiguration
XRpc2,
/// <summary>
/// 国际服Hoyolab请求配置
/// Hoyolab app
/// </summary>
XRpc3,
}

View File

@@ -68,10 +68,26 @@ internal static partial class IocHttpClientConfiguration
/// <summary>
/// 对于需要添加动态密钥的客户端使用此配置
/// 国际服 API 测试
/// Hoyolab app
/// </summary>
/// <param name="client">配置后的客户端</param>
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);
}
/// <summary>
/// 对于需要添加动态密钥的客户端使用此配置
/// Hoyolab web
/// </summary>
/// <param name="client">配置后的客户端</param>
private static void XRpc4Configuration(HttpClient client)
{
client.Timeout = Timeout.InfiniteTimeSpan;
client.DefaultRequestHeaders.UserAgent.ParseAdd(HoyolabOptions.UserAgentOversea);

View File

@@ -87,7 +87,7 @@ internal static class ApiOsEndpoints
#endregion
#region ApiOsTaKumiApi
#region ApiOsTakumiBindingApi
/// <summary>
/// 用户游戏角色
@@ -105,6 +105,12 @@ internal static class ApiOsEndpoints
return $"{ApiAccountOsBindingApi}/getUserGameRolesByLtoken?game_biz=hk4e_global&region={region}";
}
/// <summary>
/// Game Authkey
/// </summary>
public const string BindingGenAuthKey = $"{ApiAccountOsBindingApi}/genAuthKey";
#endregion
#region BbsApiOsApi
@@ -119,6 +125,11 @@ internal static class ApiOsEndpoints
return $"{BbsApiOs}/community/painter/wapi/user/full";
}
/// <summary>
/// 用户详细信息
/// </summary>
public const string UserFullInfo = $"{BbsApiOs}/community/user/wapi/getUserFullInfo?gid=2";
/// <summary>
/// 国际服角色基本信息
/// </summary>
@@ -126,13 +137,13 @@ internal static class ApiOsEndpoints
/// <returns>角色基本信息字符串</returns>
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}";
}
/// <summary>
/// 国际服角色信息
/// </summary>
public const string GameRecordCharacter = $"{BbsApiOsGameRecordApi}/character";
public const string GameRecordCharacter = $"{BbsApiOsGameRecordAppApi}/character";
/// <summary>
/// 国际服游戏记录实时便笺
@@ -141,7 +152,7 @@ internal static class ApiOsEndpoints
/// <returns>游戏记录实时便笺字符串</returns>
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}";
}
/// <summary>
@@ -151,7 +162,7 @@ internal static class ApiOsEndpoints
/// <returns>游戏记录主页字符串</returns>
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}";
}
/// <summary>
@@ -162,7 +173,7 @@ internal static class ApiOsEndpoints
/// <returns>深渊信息字符串</returns>
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
/// </summary>
public const string ActHoyolabReferer = "https://act.hoyolab.com/";
/// <summary>
/// App hoyolab referer
/// </summary>
public const string AppHoyolabReferer = "https://app.hoyolab.com/";
#endregion
}

View File

@@ -47,22 +47,4 @@ internal sealed partial class UserClient : IUserClient
return Response.Response.DefaultIfNull(resp);
}
/// <summary>
/// 获取当前用户详细信息,使用 LToken
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>详细信息</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSK2)]
public async Task<Response<UserFullInfoWrapper>> GetOsUserFullInfoAsync(Model.Entity.User user, CancellationToken token = default)
{
Response<UserFullInfoWrapper>? resp = await httpClient
.SetUser(user, CookieType.LToken)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false)
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiOsEndpoints.UserFullInfoQuery(user.Aid!), options, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
}

View File

@@ -14,6 +14,11 @@ internal enum SaltType
/// </summary>
None,
/// <summary>
/// PROD
/// </summary>
PROD,
/// <summary>
/// X4
/// </summary>
@@ -24,11 +29,6 @@ internal enum SaltType
/// </summary>
X6,
/// <summary>
/// PROD
/// </summary>
PROD,
/// <summary>
/// K2
/// </summary>
@@ -40,7 +40,22 @@ internal enum SaltType
LK2,
/// <summary>
/// Hoyolab K2
/// OSK2
/// </summary>
OSK2,
/// <summary>
/// OSLK
/// </summary>
OSLK2,
/// <summary>
/// OSX4
/// </summary>
OSX4,
/// <summary>
/// OSX6
/// </summary>
OSX6,
}

View File

@@ -41,19 +41,23 @@ internal sealed class HoyolabOptions : IOptions<HoyolabOptions>
/// <summary>
/// Hoyolab Rpc 版本
/// </summary>
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<SaltType, string> SaltsInner = new Dictionary<SaltType, string>()
{
// 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;

View File

@@ -37,12 +37,12 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>实时便笺</returns>
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSK2)]
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSX4)]
public async Task<Response<DailyNote.DailyNote>> GetDailyNoteAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<DailyNote.DailyNote>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false)
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false)
.TryCatchGetFromJsonAsync<Response<DailyNote.DailyNote>>(ApiOsEndpoints.GameRecordDailyNote(userAndUid.Uid.Value), options, logger, token)
.ConfigureAwait(false);
@@ -55,12 +55,12 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="userAndUid">用户与角色</param>
/// <param name="token">取消令牌</param>
/// <returns>玩家的基础信息</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSK2)]
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSX4)]
public async Task<Response<PlayerInfo>> GetPlayerInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
Response<PlayerInfo>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false)
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false)
.TryCatchGetFromJsonAsync<Response<PlayerInfo>>(ApiOsEndpoints.GameRecordIndex(userAndUid.Uid), options, logger, token)
.ConfigureAwait(false);
@@ -74,12 +74,12 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="schedule">1当期2上期</param>
/// <param name="token">取消令牌</param>
/// <returns>深渊信息</returns>
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSK2)]
[ApiInformation(Cookie = CookieType.Cookie, Salt = SaltType.OSX4)]
public async Task<Response<SpiralAbyss.SpiralAbyss>> GetSpiralAbyssAsync(UserAndUid userAndUid, SpiralAbyssSchedule schedule, CancellationToken token = default)
{
Response<SpiralAbyss.SpiralAbyss>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false)
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false)
.TryCatchGetFromJsonAsync<Response<SpiralAbyss.SpiralAbyss>>(ApiOsEndpoints.GameRecordSpiralAbyss(schedule, userAndUid.Uid), options, logger, token)
.ConfigureAwait(false);
@@ -93,14 +93,14 @@ internal sealed partial class GameRecordClientOversea : IGameRecordClient
/// <param name="playerInfo">玩家的基础信息</param>
/// <param name="token">取消令牌</param>
/// <returns>角色列表</returns>
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSK2)]
[ApiInformation(Cookie = CookieType.LToken, Salt = SaltType.OSX4)]
public async Task<Response<CharacterWrapper>> GetCharactersAsync(UserAndUid userAndUid, PlayerInfo playerInfo, CancellationToken token = default)
{
CharacterData data = new(userAndUid.Uid, playerInfo.Avatars.Select(x => x.Id));
Response<CharacterWrapper>? resp = await httpClient
.SetUser(userAndUid.User, CookieType.Cookie)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.OSK2, false)
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.OSX4, false)
.TryCatchPostAsJsonAsync<CharacterData, Response<CharacterWrapper>>(ApiOsEndpoints.GameRecordCharacter, data, options, logger, token)
.ConfigureAwait(false);