mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
@@ -25,7 +25,7 @@ internal enum HttpClientConfiguration
|
||||
XRpc2,
|
||||
|
||||
/// <summary>
|
||||
/// 国际服Hoyolab请求配置
|
||||
/// Hoyolab app
|
||||
/// </summary>
|
||||
XRpc3,
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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®ion={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
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user