support getWidgetData for #207

This commit is contained in:
DismissedLight
2022-11-13 15:38:02 +08:00
parent 9205d51cd5
commit a150c4a04c
19 changed files with 272 additions and 16 deletions

View File

@@ -46,6 +46,6 @@ internal static class IocConfiguration
}
}
return services.AddDbContextPool<AppDbContext>(builder => builder.UseSqlite(sqlConnectionString));
return services.AddDbContext<AppDbContext>(builder => builder.UseSqlite(sqlConnectionString));
}
}

View File

@@ -54,11 +54,12 @@ public static class Must
/// <summary>
/// Unconditionally throws an <see cref="NotSupportedException"/>.
/// </summary>
/// <param name="context">上下文</param>
/// <returns>Nothing. This method always throws.</returns>
[DoesNotReturn]
public static System.Exception NeverHappen()
public static System.Exception NeverHappen(string? context = null)
{
throw new NotSupportedException("该行为不应发生,请联系开发者进一步确认");
throw new NotSupportedException(context);
}
/// <summary>

View File

@@ -182,7 +182,7 @@ internal sealed class ExtendedWindow<TWindow> : IRecipient<BackdropTypeChangedMe
// workaround for https://github.com/microsoft/WindowsAppSDK/issues/2976
SizeInt32 size = appWindow.ClientSize;
size.Height -= 38;
size.Height -= (int)(31 * scale);
appWindow.ResizeClient(size);
}
}

View File

@@ -7,6 +7,7 @@ using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Intrinsic;
using Snap.Hutao.Model.Metadata.Abstraction;
using Snap.Hutao.Model.Metadata.Converter;
using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Avatar;
@@ -18,7 +19,7 @@ public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
/// <summary>
/// Id
/// </summary>
public int Id { get; set; }
public AvatarId Id { get; set; }
/// <summary>
/// 排序号
@@ -88,6 +89,7 @@ public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
/// <summary>
/// [非元数据] 搭配数据
/// </summary>
[JsonIgnore]
public ComplexAvatarCollocation? Collocation { get; set; }
/// <summary>
@@ -118,6 +120,7 @@ public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
Icon = AvatarIconConverter.IconNameToUri(Icon),
Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
Quality = Quality,
Count = count,
};
}
@@ -137,6 +140,7 @@ public class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQuality
Icon = AvatarIconConverter.IconNameToUri(Icon),
Badge = ElementNameIconConverter.ElementNameToIconUri(FetterInfo.VisionBefore),
Quality = Quality,
Time = time,
LastPull = lastPull,
IsUp = isUp,

View File

@@ -11,6 +11,7 @@ namespace Snap.Hutao.Model.Metadata.Converter;
internal class ElementNameIconConverter : ValueConverterBase<string, Uri>
{
private const string BaseUrl = "https://static.snapgenshin.com/IconElement/UI_Icon_Element_{0}.png";
private static readonly Uri UIIconNone = new("https://static.snapgenshin.com/Bg/UI_Icon_None.png");
/// <summary>
/// 将中文元素名称转换为图标链接
@@ -28,10 +29,12 @@ internal class ElementNameIconConverter : ValueConverterBase<string, Uri>
"岩" => "Rock",
"水" => "Water",
"风" => "Wind",
_ => throw Must.NeverHappen(),
_ => string.Empty,
};
return new Uri(string.Format(BaseUrl, element));
return string.IsNullOrEmpty(element)
? UIIconNone
: new Uri(string.Format(BaseUrl, element));
}
/// <inheritdoc/>

View File

@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Primitive.Converter;
using Windows.ApplicationModel.Chat;
namespace Snap.Hutao.Model.Primitive;
@@ -62,4 +63,4 @@ public readonly struct AvatarId : IEquatable<AvatarId>
{
return Value.GetHashCode();
}
}
}

View File

@@ -0,0 +1,23 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Snap.Hutao.Web.Hoyolab.Annotation;
/// <summary>
/// API 信息
/// </summary>
/// <typeparam name="TReturnType">API 的返回类型</typeparam>
[AttributeUsage(AttributeTargets.Method)]
internal class ApiInformationAttribute<TReturnType> : Attribute
{
/// <summary>
/// Cookie类型
/// </summary>
public CookieType Cookie { get; set; }
}

View File

@@ -145,13 +145,25 @@ internal static class ApiEndpoints
// https://sdk-static.mihoyo.com/hk4e_cn/mdk/launcher/api/content?key=eYd89JmJ&language=zh-cn&launcher_id=18
#endregion
#region App
/// <summary>
/// 小组件数据
/// </summary>
public const string AppWidgetData = $"{AppCardApi}/getWidgetData?game_id=2";
#endregion
// consts
private const string ApiTakumi = "https://api-takumi.mihoyo.com";
private const string ApiTakumiAuthApi = $"{ApiTakumi}/auth/api";
private const string ApiTaKumiBindingApi = $"{ApiTakumi}/binding/api";
private const string ApiTakumiRecord = "https://api-takumi-record.mihoyo.com";
private const string ApiTakumiRecordApi = $"{ApiTakumiRecord}/game_record/app/genshin/api";
private const string App = "https://app.mihoyo.com";
private const string AppCardApi = $"{App}/game_record/app/card/api";
private const string BbsApi = "https://bbs-api.mihoyo.com";
private const string BbsApiUserApi = $"{BbsApi}/user/wapi";

View File

@@ -0,0 +1,51 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.Model.Entity;
using Snap.Hutao.Web.Hoyolab.Annotation;
using Snap.Hutao.Web.Response;
using System.Net.Http;
namespace Snap.Hutao.Web.Hoyolab.App.GameRecord;
/// <summary>
/// 桌面卡片客户端
/// </summary>
[HttpClient(HttpClientConfigration.XRpc)]
internal class CardClient
{
private readonly HttpClient httpClient;
private readonly JsonSerializerOptions options;
private readonly ILogger<CardClient> logger;
/// <summary>
/// 构造一个新的桌面卡片客户端
/// </summary>
/// <param name="httpClient">http客户端</param>
/// <param name="options">选项</param>
/// <param name="logger">日志器</param>
public CardClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<CardClient> logger)
{
this.httpClient = httpClient;
this.options = options;
this.logger = logger;
}
/// <summary>
/// 异步获取桌面小组件数据
/// </summary>
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>桌面小组件数据</returns>
[ApiInformation<WidgetData>(Cookie = CookieType.Stoken)]
public async Task<WidgetData?> GetWidgetDataAsync(User user, CancellationToken token)
{
Response<DataWrapper<WidgetData>>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
.TryCatchGetFromJsonAsync<Response<DataWrapper<WidgetData>>>(ApiEndpoints.UserFullInfo, options, logger, token)
.ConfigureAwait(false);
return resp?.Data?.Data;
}
}

View File

@@ -0,0 +1,60 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
namespace Snap.Hutao.Web.Hoyolab.App.GameRecord;
/// <summary>
/// 小组件数据
/// </summary>
public class WidgetData
{
/// <summary>
/// 游戏Id 2
/// </summary>
[JsonPropertyName("game_id")]
public int GameId { get; set; }
/// <summary>
/// 游戏内Uid
/// </summary>
[JsonPropertyName("game_role_id")]
public string GameRoleId { get; set; } = default!;
/// <summary>
/// 昵称
/// </summary>
[JsonPropertyName("nickname")]
public string Nickname { get; set; } = default!;
/// <summary>
/// 区服
/// </summary>
[JsonPropertyName("region")]
public string Region { get; set; } = default!;
/// <summary>
/// 冒险等阶
/// </summary>
[JsonPropertyName("level")]
public int Level { get; set; }
/// <summary>
/// 背景图片
/// </summary>
[JsonPropertyName("background_image")]
public string BackgroundImage { get; set; } = default!;
/// <summary>
/// 数据
/// </summary>
[JsonPropertyName("data")]
public List<WidgetDataItem> Data { get; set; } = default!;
/// <summary>
/// 区服名称
/// </summary>
[JsonPropertyName("region_name")]
public string RegionName { get; set; } = default!;
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
namespace Snap.Hutao.Web.Hoyolab.App.GameRecord;
/// <summary>
/// 小组件数据项
/// </summary>
public class WidgetDataItem
{
/// <summary>
/// 项目名称
/// </summary>
[JsonPropertyName("name")]
public string Name { get; set; } = default!;
/// <summary>
/// 类型 均为 "String"
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; } = default!;
/// <summary>
/// 显示值
/// </summary>
[JsonPropertyName("value")]
public string Value { get; set; } = default!;
}

View File

@@ -39,10 +39,10 @@ internal class UserClient
public async Task<UserInfo?> GetUserFullInfoAsync(Model.Binding.User.User user, CancellationToken token = default)
{
Response<UserFullInfoWrapper>? resp = await httpClient
.SetUser(user)
.SetReferer(ApiEndpoints.BbsReferer)
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfo, options, logger, token)
.ConfigureAwait(false);
.SetUser(user)
.SetReferer(ApiEndpoints.BbsReferer)
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfo, options, logger, token)
.ConfigureAwait(false);
return resp?.Data?.UserInfo;
}

View File

@@ -21,4 +21,6 @@ public partial class Cookie
public const string STOKEN = "stoken";
public const string STUID = "stuid";
public const string MID = "mid";
}

View File

@@ -192,12 +192,32 @@ public partial class Cookie
}
}
/// <summary>
/// 根据类型输出对应的Cookie
/// </summary>
/// <param name="type">类型</param>
/// <returns>Cookie对应的字符串表示</returns>
public string ToString(CookieType type)
{
IEnumerable<KeyValuePair<string, string>> results;
results = type switch
{
CookieType.None => Enumerable.Empty<KeyValuePair<string, string>>(),
CookieType.Stoken => inner.Where(kvp => kvp.Key is STUID or STOKEN or MID),
CookieType.All => inner,
_ => throw Must.NeverHappen(type.ToString()),
};
return string.Join(';', results.Select(kvp => $"{kvp.Key}={kvp.Value}"));
}
/// <summary>
/// 转换为Cookie的字符串表示
/// </summary>
/// <returns>Cookie的字符串表示</returns>
public override string ToString()
{
return string.Join(';', inner.Select(kvp => $"{kvp.Key}={kvp.Value}"));
return ToString(CookieType.All);
}
}

View File

@@ -0,0 +1,25 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hoyolab;
/// <summary>
/// Cookie 类型
/// </summary>
public enum CookieType
{
/// <summary>
/// 不需要 Cookie
/// </summary>
None,
/// <summary>
/// 需要 Stoken
/// </summary>
Stoken,
/// <summary>
/// 全部
/// </summary>
All,
}

View File

@@ -66,6 +66,7 @@ internal static class HttpClientExtensions
/// <param name="httpClient">http客户端</param>
/// <param name="user">绑定用户</param>
/// <returns>客户端</returns>
[Obsolete("请使用带有 type 的重载")]
internal static HttpClient SetUser(this HttpClient httpClient, User user)
{
httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString());
@@ -77,10 +78,11 @@ internal static class HttpClientExtensions
/// </summary>
/// <param name="httpClient">http客户端</param>
/// <param name="user">实体用户</param>
/// <param name="type">Cookie类型</param>
/// <returns>客户端</returns>
internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user)
internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user, CookieType type = CookieType.All)
{
httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString());
httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString(type));
return httpClient;
}

View File

@@ -0,0 +1,17 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Response;
/// <summary>
/// 数据对象包装器
/// </summary>
/// <typeparam name="T">数据类型</typeparam>
public class DataWrapper<T>
{
/// <summary>
/// 数据
/// </summary>
[JsonPropertyName("data")]
public T Data { get; set; } = default!;
}

View File

@@ -23,6 +23,11 @@ public enum KnownReturnCode : int
/// </summary>
RET_NEED_RISK_VERIFY = -3235,
/// <summary>
/// 验证码已失效,请重新获取
/// </summary>
VerifyCodeOutdated = -3209,
/// <summary>
/// 禁止
/// </summary>

View File

@@ -14,4 +14,4 @@ public class ListWrapper<T>
/// </summary>
[JsonPropertyName("list")]
public List<T> List { get; set; } = default!;
}
}