sign-in endpoints

This commit is contained in:
Lightczx
2023-12-01 10:43:06 +08:00
parent af87891a5f
commit 176c26df51
15 changed files with 207 additions and 131 deletions

View File

@@ -24,20 +24,15 @@
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
<FlyoutBase.AttachedFlyout>
<Flyout LightDismissOverlayMode="On" Placement="Full">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid>
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource ChineseSource="http://webstatic.mihoyo.com/ys/event/e20200923adopt_calculator/index.html?bbs_presentation_style=fullscreen&amp;bbs_auth_required=true&amp;utm_source=bbs&amp;utm_medium=mys&amp;utm_campaign=GameRecord"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Grid>
<Flyout
FlyoutPresenterStyle="{ThemeResource WebViewerFlyoutPresenterStyle}"
LightDismissOverlayMode="On"
Placement="Full">
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource ChineseSource="http://webstatic.mihoyo.com/ys/event/e20200923adopt_calculator/index.html?bbs_presentation_style=fullscreen&amp;bbs_auth_required=true&amp;utm_source=bbs&amp;utm_medium=mys&amp;utm_campaign=GameRecord"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Flyout>
</FlyoutBase.AttachedFlyout>
</cwc:SettingsCard>
@@ -50,22 +45,34 @@
</mxi:Interaction.Behaviors>
<FlyoutBase.AttachedFlyout>
<Flyout
FlyoutPresenterStyle="{ThemeResource WebViewerFlyoutPresenterStyle}"
LightDismissOverlayMode="On"
Placement="Full"
ShouldConstrainToRootBounds="False">
<Flyout.FlyoutPresenterStyle>
<Style TargetType="FlyoutPresenter">
<Setter Property="Padding" Value="0"/>
<Setter Property="BorderBrush" Value="{ThemeResource CardStrokeColorDefaultBrush}"/>
</Style>
</Flyout.FlyoutPresenterStyle>
<Grid>
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource ChineseSource="https://webstatic.mihoyo.com/app/community-game-records/index.html"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Grid>
Placement="Full">
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource ChineseSource="https://webstatic.mihoyo.com/app/community-game-records/index.html"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Flyout>
</FlyoutBase.AttachedFlyout>
</cwc:SettingsCard>
<cwc:SettingsCard Header="SignIn Reward" IsClickEnabled="True">
<mxi:Interaction.Behaviors>
<mxic:EventTriggerBehavior EventName="Click">
<shcb:ShowAttachedFlyoutAction/>
</mxic:EventTriggerBehavior>
</mxi:Interaction.Behaviors>
<FlyoutBase.AttachedFlyout>
<Flyout
FlyoutPresenterStyle="{ThemeResource WebViewerFlyoutPresenterStyle}"
LightDismissOverlayMode="On"
Placement="Full">
<shvc:WebViewer>
<shvc:WebViewer.SourceProvider>
<shvc:StaticWebview2ViewerSource ChineseSource="https://act.mihoyo.com/bbs/event/signin/hk4e/index.html?act_id=e202311201442471"/>
</shvc:WebViewer.SourceProvider>
</shvc:WebViewer>
</Flyout>
</FlyoutBase.AttachedFlyout>
</cwc:SettingsCard>

View File

@@ -18,7 +18,7 @@ internal sealed class DailyNoteWebViewerSource : IWebViewerSource
public string GetSource(UserAndUid userAndUid)
{
string query = userAndUid.Uid.ToQueryString();
string query = userAndUid.Uid.ToRoleIdServerQueryString();
return $"https://webstatic.mihoyo.com/app/community-game-records/index.html?bbs_presentation_style=fullscreen#/ys/daily/?{query}";
}
}

View File

@@ -144,49 +144,6 @@ internal static class ApiEndpoints
}
#endregion
#region ApiTakumiEventBbsSignReward
/// <summary>
/// 签到活动Id
/// </summary>
public const string SignInRewardActivityId = "e202009291139501";
/// <summary>
/// 签到
/// </summary>
public const string SignInRewardHome = $"{ApiTakumi}/event/bbs_sign_reward/home?act_id={SignInRewardActivityId}";
/// <summary>
/// 签到
/// </summary>
public const string SignInRewardSign = $"{ApiTakumi}/event/bbs_sign_reward/sign";
/// <summary>
/// 补签
/// </summary>
public const string SignInRewardReSign = $"{ApiTakumi}/event/bbs_sign_reward/resign";
/// <summary>
/// 补签信息
/// </summary>
/// <param name="uid">uid</param>
/// <returns>补签信息字符串</returns>
public static string SignInRewardResignInfo(in PlayerUid uid)
{
return $"{ApiTakumi}/event/bbs_sign_reward/resign_info?act_id={SignInRewardActivityId}&region={uid.Region}&uid={uid.Value}";
}
/// <summary>
/// 签到信息
/// </summary>
/// <param name="uid">uid</param>
/// <returns>签到信息字符串</returns>
public static string SignInRewardInfo(in PlayerUid uid)
{
return $"{ApiTakumi}/event/bbs_sign_reward/info?act_id={SignInRewardActivityId}&region={uid.Region}&uid={uid.Value}";
}
#endregion
#region ApiTakumiEventCalculate
/// <summary>
@@ -253,6 +210,34 @@ internal static class ApiEndpoints
public const string CalculateWeaponList = $"{ApiTakumiEventCalculate}/v1/weapon/list";
#endregion
#region ApiTakumiEventLuna
public const string LunaActivityId = "e202311201442471";
public static string LunaHome(string languageCode)
{
return $"{ApiTakumiEventLuna}/home?lang={languageCode}&act_id={LunaActivityId}";
}
public const string LunaSign = $"{ApiTakumiEventLuna}/sign";
public const string LunaReSign = $"{ApiTakumiEventLuna}/resign";
public static string LunaExtraAward(in PlayerUid uid, string languageCode)
{
return $"{ApiTakumiEventLuna}/home?act_id={LunaActivityId}&{uid.ToUidRegionQueryString()}&lang={languageCode}";
}
public static string LunaResignInfo(in PlayerUid uid)
{
return $"{ApiTakumiEventLuna}/resign_info?act_id={LunaActivityId}&{uid.ToUidRegionQueryString()}";
}
public static string LunaInfo(in PlayerUid uid, string languageCode)
{
return $"{ApiTakumiEventLuna}/info?lang={languageCode}&act_id={LunaActivityId}&{uid.ToUidRegionQueryString()}";
}
#endregion
#region AppAuthApi
/// <summary>
@@ -382,16 +367,14 @@ internal static class ApiEndpoints
private const string ApiTakumiEvent = $"{ApiTakumi}/event";
private const string ApiTakumiEventCalculate = $"{ApiTakumiEvent}/e20200928calculate";
private const string ApiTakumiEventLuna = $"{ApiTakumiEvent}/luna";
private const string ApiTakumiRecord = "https://api-takumi-record.mihoyo.com";
private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api";
private const string ApiTakumiRecordAapi = $"{ApiTakumiRecord}/game_record/app/genshin/aapi";
/// <summary>
/// Referer: https://app.mihoyo.com
/// </summary>
public const string AppMihoyoReferer = "https://app.mihoyo.com";
private const string AppAuthApi = $"{AppMihoyoReferer}/account/auth/api";
public const string AppMihoyoReferer = "https://app.mihoyo.com";
private const string BbsApi = "https://bbs-api.mihoyo.com";
private const string BbsApiUserApi = $"{BbsApi}/user/wapi";

View File

@@ -8,7 +8,7 @@ namespace Snap.Hutao.Web.Hoyolab;
internal static class PlayerUidExtension
{
public static string ToQueryString(this in PlayerUid playerUid)
public static string ToRoleIdServerQueryString(this in PlayerUid playerUid)
{
NameValueCollection collection = [];
collection.Set("role_id", playerUid.Value);
@@ -16,4 +16,13 @@ internal static class PlayerUidExtension
return collection.ToQueryString();
}
public static string ToUidRegionQueryString(this in PlayerUid playerUid)
{
NameValueCollection collection = [];
collection.Set("uid", playerUid.Value);
collection.Set("region", playerUid.Region);
return collection.ToQueryString();
}
}

View File

@@ -6,7 +6,7 @@ namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
/// <summary>
/// 奖励物品
/// </summary>
internal sealed class Award
internal class Award
{
/// <summary>
/// 图标

View File

@@ -0,0 +1,13 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
internal sealed class ExtraAward : Award
{
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("sign_day")]
public int SignDay { get; set; }
}

View File

@@ -0,0 +1,22 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
internal sealed class ExtraAwardInfo
{
[JsonPropertyName("awards")]
public List<ExtraAward> Awards { get; set; } = default!;
[JsonPropertyName("total_cnt")]
public int TotalCount { get; set; }
[JsonPropertyName("ys_first_award")]
public bool YsFirstAward { get; set; }
[JsonPropertyName("has_short_act")]
public bool HasShortAct { get; set; }
[JsonPropertyName("short_act_info")]
public ShortActInfo ShortActInfo { get; set; } = default!;
}

View File

@@ -17,9 +17,15 @@ internal sealed class Reward
[JsonPropertyName("awards")]
public List<Award> Awards { get; set; } = default!;
[JsonPropertyName("biz")]
public string Biz { get; set; } = default!;
/// <summary>
/// 支持补签
/// </summary>
[JsonPropertyName("resign")]
public bool Resign { get; set; }
[JsonPropertyName("short_extra_award")]
public ShortExtraAward ShortExtraAward { get; set; } = default!;
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
internal sealed class ShortActInfo
{
[JsonPropertyName("awards")]
public List<JsonElement> Awards { get; set; } = default!;
[JsonPropertyName("start_timestamp")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public long StartTimestamp { get; set; }
[JsonPropertyName("end_timestamp")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public long EndTimestamp { get; set; }
[JsonPropertyName("total_cnt")]
public int TotalCount { get; set; }
}

View File

@@ -0,0 +1,27 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
internal sealed class ShortExtraAward
{
[JsonPropertyName("has_extra_award")]
public bool HasExtraAward { get; set; }
[JsonPropertyName("start_time")]
public string StartTime { get; set; } = default!;
[JsonPropertyName("end_time")]
public string EndTime { get; set; } = default!;
[JsonPropertyName("list")]
public List<JsonElement> List { get; set; } = default!;
[JsonPropertyName("start_timestamp")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public long StartTimestamp { get; set; } = default!;
[JsonPropertyName("end_timestamp")]
[JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public long EndTimestamp { get; set; } = default!;
}

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Service.Metadata;
using Snap.Hutao.ViewModel.User;
using Snap.Hutao.Web.Hoyolab.DataSigning;
using Snap.Hutao.Web.Hutao.Geetest;
@@ -22,14 +23,33 @@ internal sealed partial class SignInClient : ISignInClient
{
private readonly IHttpRequestMessageBuilderFactory httpRequestMessageBuilderFactory;
private readonly HomaGeetestClient homaGeetestClient;
private readonly MetadataOptions metadataOptions;
private readonly ILogger<SignInClient> logger;
private readonly HttpClient httpClient;
public async ValueTask<Response<ExtraAwardInfo>> GetExtraAwardInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.LunaExtraAward(userAndUid.Uid, metadataOptions.LanguageCode))
.SetUserCookieAndFpHeader(userAndUid, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.Get();
await builder.SignDataAsync(DataSignAlgorithmVersion.Gen1, SaltType.LK2, true).ConfigureAwait(false);
Response<ExtraAwardInfo>? resp = await builder
.TryCatchSendAsync<Response<ExtraAwardInfo>>(httpClient, logger, token)
.ConfigureAwait(false);
return Response.Response.DefaultIfNull(resp);
}
public async ValueTask<Response<SignInRewardInfo>> GetInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.SignInRewardInfo(userAndUid.Uid))
.SetRequestUri(ApiEndpoints.LunaInfo(userAndUid.Uid, metadataOptions.LanguageCode))
.SetUserCookieAndFpHeader(userAndUid, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.Get();
await builder.SignDataAsync(DataSignAlgorithmVersion.Gen1, SaltType.LK2, true).ConfigureAwait(false);
@@ -44,8 +64,9 @@ internal sealed partial class SignInClient : ISignInClient
public async ValueTask<Response<SignInRewardReSignInfo>> GetResignInfoAsync(UserAndUid userAndUid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.SignInRewardResignInfo(userAndUid.Uid))
.SetRequestUri(ApiEndpoints.LunaResignInfo(userAndUid.Uid))
.SetUserCookieAndFpHeader(userAndUid, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.Get();
await builder.SignDataAsync(DataSignAlgorithmVersion.Gen1, SaltType.LK2, true).ConfigureAwait(false);
@@ -60,8 +81,9 @@ internal sealed partial class SignInClient : ISignInClient
public async ValueTask<Response<Reward>> GetRewardAsync(Model.Entity.User user, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.SignInRewardHome)
.SetRequestUri(ApiEndpoints.LunaHome(metadataOptions.LanguageCode))
.SetUserCookieAndFpHeader(user, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.Get();
Response<Reward>? resp = await builder
@@ -74,8 +96,9 @@ internal sealed partial class SignInClient : ISignInClient
public async ValueTask<Response<SignInResult>> ReSignAsync(UserAndUid userAndUid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.SignInRewardReSign)
.SetRequestUri(ApiEndpoints.LunaReSign)
.SetUserCookieAndFpHeader(userAndUid, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.PostJson(new SignInData(userAndUid.Uid, false));
await builder.SignDataAsync(DataSignAlgorithmVersion.Gen1, SaltType.LK2, true).ConfigureAwait(false);
@@ -90,8 +113,9 @@ internal sealed partial class SignInClient : ISignInClient
public async ValueTask<Response<SignInResult>> SignAsync(UserAndUid userAndUid, CancellationToken token = default)
{
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.SignInRewardSign)
.SetRequestUri(ApiEndpoints.LunaSign)
.SetUserCookieAndFpHeader(userAndUid, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.PostJson(new SignInData(userAndUid.Uid, false));
await builder.SignDataAsync(DataSignAlgorithmVersion.Gen1, SaltType.LK2, true).ConfigureAwait(false);
@@ -107,8 +131,9 @@ internal sealed partial class SignInClient : ISignInClient
if (verifyResponse is { Code: 0, Data: { Validate: string validate, Challenge: string challenge } })
{
HttpRequestMessageBuilder verifiedBuilder = httpRequestMessageBuilderFactory.Create()
.SetRequestUri(ApiEndpoints.SignInRewardSign)
.SetRequestUri(ApiEndpoints.LunaSign)
.SetUserCookieAndFpHeader(userAndUid, CookieType.CookieToken)
.SetHeader("x-rpc-signgame", "hk4e")
.SetXrpcChallenge(challenge, validate)
.PostJson(new SignInData(userAndUid.Uid, false));

View File

@@ -16,7 +16,7 @@ internal sealed class SignInData
[SuppressMessage("", "SH002")]
public SignInData(PlayerUid uid, bool isOversea)
{
ActivityId = isOversea ? ApiOsEndpoints.SignInRewardActivityId : ApiEndpoints.SignInRewardActivityId;
ActivityId = isOversea ? ApiOsEndpoints.SignInRewardActivityId : ApiEndpoints.LunaActivityId;
Region = uid.Region;
Uid = uid.Value;
}

View File

@@ -3,38 +3,23 @@
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
/// <summary>
/// 签到结果
/// </summary>
internal sealed class SignInResult
{
/// <summary>
/// 通常是 ""
/// </summary>
[JsonPropertyName("code")]
public string Code { get; set; } = default!;
/// <summary>
/// 通常是 0
/// </summary>
[JsonPropertyName("risk_code")]
public int RiskCode { get; set; }
/// <summary>
/// 通常是 ""
/// </summary>
[JsonPropertyName("gt")]
public string Gt { get; set; } = default!;
/// <summary>
/// 通常是 ""
/// </summary>
[JsonPropertyName("challenge")]
public string Challenge { get; set; } = default!;
/// <summary>
/// 通常是 ""
/// </summary>
[JsonPropertyName("success")]
public int Success { get; set; }
[JsonPropertyName("is_risk")]
public bool IsRisk { get; set; }
}

View File

@@ -3,14 +3,8 @@
namespace Snap.Hutao.Web.Hoyolab.Takumi.Event.BbsSignReward;
/// <summary>
/// 签到信息
/// </summary>
internal sealed class SignInRewardInfo
{
/// <summary>
/// 累积签到天数
/// </summary>
[JsonPropertyName("total_sign_day")]
public int TotalSignDay { get; set; }
@@ -20,33 +14,17 @@ internal sealed class SignInRewardInfo
[JsonPropertyName("today")]
public string? Today { get; set; }
/// <summary>
/// 今日是否已签到
/// </summary>
[JsonPropertyName("is_sign")]
public bool IsSign { get; set; }
/// <summary>
///
/// </summary>
[JsonPropertyName("is_sub")]
public bool IsSub { get; set; }
/// <summary>
/// 是否首次绑定
/// </summary>
[JsonPropertyName("first_bind")]
public bool FirstBind { get; set; }
public string Region { get; set; } = default!;
/// <summary>
/// 是否为当月第一次
/// </summary>
[JsonPropertyName("month_first")]
public bool MonthFirst { get; set; }
/// <summary>
/// 漏签天数
/// </summary>
[JsonPropertyName("sign_cnt_missed")]
public int SignCountMissed { get; set; }
[JsonPropertyName("short_sign_day")]
public int ShortSignDay { get; set; }
}

View File

@@ -24,7 +24,7 @@ internal static class NameValueCollectionExtension
string? key = keys[i];
if (collection.GetValues(key) is { } values)
{
foreach (string value in values)
foreach (ref readonly string value in values.AsSpan())
{
if (!string.IsNullOrEmpty(key))
{