mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
web bridge check point
This commit is contained in:
@@ -30,8 +30,13 @@ public static class CastTo<TTo>
|
||||
|
||||
private static Func<TCachedFrom, TTo> Get()
|
||||
{
|
||||
// 参数表达式,表示 传入源类型
|
||||
ParameterExpression param = Expression.Parameter(typeof(TCachedFrom));
|
||||
|
||||
// 一元转换 调用 相关类的显式或隐式转换运算符
|
||||
UnaryExpression convert = Expression.ConvertChecked(param, typeof(TTo));
|
||||
|
||||
// 生成一个源类型入,目标类型出的 lamdba
|
||||
return Expression.Lambda<Func<TCachedFrom, TTo>>(convert, param).Compile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Hutao.Model;
|
||||
using System.Text;
|
||||
|
||||
@@ -17,7 +18,7 @@ public class ComplexReliquarySet
|
||||
/// </summary>
|
||||
/// <param name="reliquarySetRate">圣遗物套装率</param>
|
||||
/// <param name="idReliquarySetMap">圣遗物套装映射</param>
|
||||
public ComplexReliquarySet(ItemRate<ReliquarySets, double> reliquarySetRate, Dictionary<int, Metadata.Reliquary.ReliquarySet> idReliquarySetMap)
|
||||
public ComplexReliquarySet(ItemRate<ReliquarySets, double> reliquarySetRate, Dictionary<EquipAffixId, Metadata.Reliquary.ReliquarySet> idReliquarySetMap)
|
||||
{
|
||||
ReliquarySets sets = reliquarySetRate.Item;
|
||||
|
||||
@@ -43,7 +44,7 @@ public class ComplexReliquarySet
|
||||
}
|
||||
else
|
||||
{
|
||||
Name = "无圣遗物";
|
||||
Name = "无圣遗物或散件";
|
||||
}
|
||||
|
||||
Rate = $"{reliquarySetRate.Rate:P3}";
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Hutao.Model;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Hutao;
|
||||
@@ -16,7 +17,7 @@ internal class ComplexTeamRank
|
||||
/// </summary>
|
||||
/// <param name="teamRank">队伍排行</param>
|
||||
/// <param name="idAvatarMap">映射</param>
|
||||
public ComplexTeamRank(TeamAppearance teamRank, Dictionary<int, Avatar> idAvatarMap)
|
||||
public ComplexTeamRank(TeamAppearance teamRank, Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
Floor = $"第 {teamRank.Floor} 层";
|
||||
Up = teamRank.Up.Select(teamRate => new Team(teamRate, idAvatarMap)).ToList();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Hutao.Model;
|
||||
|
||||
namespace Snap.Hutao.Model.Binding.Hutao;
|
||||
@@ -16,10 +17,10 @@ internal class Team : List<ComplexAvatar>
|
||||
/// </summary>
|
||||
/// <param name="team">队伍</param>
|
||||
/// <param name="idAvatarMap">映射</param>
|
||||
public Team(ItemRate<string, int> team, Dictionary<int, Avatar> idAvatarMap)
|
||||
public Team(ItemRate<string, int> team, Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
: base(4)
|
||||
{
|
||||
IEnumerable<int> ids = team.Item.Split(',').Select(i => int.Parse(i));
|
||||
IEnumerable<int> ids = team.Item.Split(',').Select(int.Parse);
|
||||
|
||||
foreach (int id in ids)
|
||||
{
|
||||
|
||||
@@ -181,7 +181,7 @@ public class User : ObservableObject
|
||||
|
||||
if (cookieToken != null)
|
||||
{
|
||||
Cookie cookieTokenCookie = Cookie.Parse($"acount_id={Entity.Aid};cookie_token={cookieToken}");
|
||||
Cookie cookieTokenCookie = Cookie.Parse($"account_id={Entity.Aid};cookie_token={cookieToken}");
|
||||
Entity.CookieToken = cookieTokenCookie;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
/// <summary>
|
||||
@@ -8,6 +10,11 @@ namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
/// </summary>
|
||||
public class ReliquaryAffix : ReliquaryAffixBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public new ReliquaryAffixId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
@@ -13,7 +14,7 @@ public class ReliquaryAffixBase
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public int Id { get; set; }
|
||||
public ReliquaryMainAffixId Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 战斗属性
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Model.Metadata.Reliquary;
|
||||
|
||||
/// <summary>
|
||||
@@ -16,7 +18,7 @@ public class ReliquarySet
|
||||
/// <summary>
|
||||
/// 装备被动Id
|
||||
/// </summary>
|
||||
public int EquipAffixId { get; set; }
|
||||
public EquipAffixId EquipAffixId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 套装名称
|
||||
|
||||
@@ -6,9 +6,9 @@ using Snap.Hutao.Model.Primitive.Converter;
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 角色Id
|
||||
/// 8位 角色Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(AvatarIdConverter))]
|
||||
[JsonConverter(typeof(IdentityConverter<AvatarId>))]
|
||||
public readonly struct AvatarId : IEquatable<AvatarId>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 角色Id转换器
|
||||
/// </summary>
|
||||
internal class AvatarIdConverter : JsonConverter<AvatarId>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override AvatarId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.GetInt32();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, AvatarId value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core.Convert;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Id 转换器
|
||||
/// </summary>
|
||||
/// <typeparam name="TWrapper">包装类型</typeparam>
|
||||
internal class IdentityConverter<TWrapper> : JsonConverter<TWrapper>
|
||||
where TWrapper : struct
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override TWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return CastTo<TWrapper>.From(reader.GetInt32());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, TWrapper value, JsonSerializerOptions options)
|
||||
{
|
||||
|
||||
writer.WriteNumberValue(CastTo<int>.From(value));
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// 武器Id转换器
|
||||
/// </summary>
|
||||
internal class WeaponIdConverter : JsonConverter<WeaponId>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override WeaponId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
return reader.GetInt32();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(Utf8JsonWriter writer, WeaponId value, JsonSerializerOptions options)
|
||||
{
|
||||
writer.WriteNumberValue(value);
|
||||
}
|
||||
}
|
||||
65
src/Snap.Hutao/Snap.Hutao/Model/Primitive/EquipAffixId.cs
Normal file
65
src/Snap.Hutao/Snap.Hutao/Model/Primitive/EquipAffixId.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 6位 装备属性Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<EquipAffixId>))]
|
||||
public readonly struct EquipAffixId : IEquatable<EquipAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EquipAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public EquipAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(EquipAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator EquipAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(EquipAffixId left, EquipAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(EquipAffixId left, EquipAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(EquipAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is EquipAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 7位 装备属性Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<ExtendedEquipAffixId>))]
|
||||
public readonly struct ExtendedEquipAffixId : IEquatable<ExtendedEquipAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExtendedEquipAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public ExtendedEquipAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(ExtendedEquipAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ExtendedEquipAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ExtendedEquipAffixId left, ExtendedEquipAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(ExtendedEquipAffixId left, ExtendedEquipAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(ExtendedEquipAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ExtendedEquipAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 6位 圣遗物副词条Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<ReliquaryAffixId>))]
|
||||
public readonly struct ReliquaryAffixId : IEquatable<ReliquaryAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReliquaryAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public ReliquaryAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(ReliquaryAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ReliquaryAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ReliquaryAffixId left, ReliquaryAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(ReliquaryAffixId left, ReliquaryAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(ReliquaryAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ReliquaryAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 5位 圣遗物主属性Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(IdentityConverter<ReliquaryMainAffixId>))]
|
||||
public readonly struct ReliquaryMainAffixId : IEquatable<ReliquaryMainAffixId>
|
||||
{
|
||||
/// <summary>
|
||||
/// 值
|
||||
/// </summary>
|
||||
public readonly int Value;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReliquaryMainAffixId"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="value">value</param>
|
||||
public ReliquaryMainAffixId(int value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public static implicit operator int(ReliquaryMainAffixId value)
|
||||
{
|
||||
return value.Value;
|
||||
}
|
||||
|
||||
public static implicit operator ReliquaryMainAffixId(int value)
|
||||
{
|
||||
return new(value);
|
||||
}
|
||||
|
||||
public static bool operator ==(ReliquaryMainAffixId left, ReliquaryMainAffixId right)
|
||||
{
|
||||
return left.Value == right.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(ReliquaryMainAffixId left, ReliquaryMainAffixId right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(ReliquaryMainAffixId other)
|
||||
{
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is ReliquaryMainAffixId other && Equals(other);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,9 @@ using Snap.Hutao.Model.Primitive.Converter;
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
/// <summary>
|
||||
/// 武器Id
|
||||
/// 5位 武器Id
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(WeaponIdConverter))]
|
||||
[JsonConverter(typeof(IdentityConverter<WeaponId>))]
|
||||
public readonly struct WeaponId : IEquatable<WeaponId>
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,7 @@ WM_NCRBUTTONDOWN
|
||||
WM_NCRBUTTONUP
|
||||
|
||||
// Type definition
|
||||
HRESULT
|
||||
MINMAXINFO
|
||||
|
||||
// Comctl32
|
||||
|
||||
@@ -6,6 +6,7 @@ using Snap.Hutao.Model;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
@@ -22,10 +23,10 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
/// </summary>
|
||||
internal class SummaryAvatarFactory
|
||||
{
|
||||
private readonly Dictionary<int, MetadataAvatar> idAvatarMap;
|
||||
private readonly Dictionary<int, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<int, ReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly Dictionary<int, MetadataWeapon> idWeaponMap;
|
||||
private readonly Dictionary<AvatarId, MetadataAvatar> idAvatarMap;
|
||||
private readonly Dictionary<WeaponId, MetadataWeapon> idWeaponMap;
|
||||
private readonly Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly List<ReliquaryLevel> reliqueryLevels;
|
||||
private readonly List<MetadataReliquary> reliquaries;
|
||||
|
||||
@@ -42,18 +43,18 @@ internal class SummaryAvatarFactory
|
||||
/// <param name="reliquaries">圣遗物</param>
|
||||
/// <param name="avatarInfo">角色信息</param>
|
||||
public SummaryAvatarFactory(
|
||||
Dictionary<int, MetadataAvatar> idAvatarMap,
|
||||
Dictionary<int, MetadataWeapon> idWeaponMap,
|
||||
Dictionary<int, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<int, ReliquaryAffix> idReliquaryAffixMap,
|
||||
Dictionary<AvatarId, MetadataAvatar> idAvatarMap,
|
||||
Dictionary<WeaponId, MetadataWeapon> idWeaponMap,
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap,
|
||||
List<ReliquaryLevel> reliqueryLevels,
|
||||
List<MetadataReliquary> reliquaries,
|
||||
ModelAvatarInfo avatarInfo)
|
||||
{
|
||||
this.idAvatarMap = idAvatarMap;
|
||||
this.idWeaponMap = idWeaponMap;
|
||||
this.idRelicMainPropMap = idRelicMainPropMap;
|
||||
this.idReliquaryAffixMap = idReliquaryAffixMap;
|
||||
this.idWeaponMap = idWeaponMap;
|
||||
this.reliqueryLevels = reliqueryLevels;
|
||||
this.reliquaries = reliquaries;
|
||||
this.avatarInfo = avatarInfo;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
@@ -33,10 +34,10 @@ internal class SummaryFactory : ISummaryFactory
|
||||
/// <inheritdoc/>
|
||||
public async Task<Summary> CreateAsync(ModelPlayerInfo playerInfo, IEnumerable<ModelAvatarInfo> avatarInfos, CancellationToken token)
|
||||
{
|
||||
Dictionary<int, MetadataAvatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<int, MetadataWeapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<int, FightProperty> idRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<int, ReliquaryAffix> idReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<AvatarId, MetadataAvatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<WeaponId, MetadataWeapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap = await metadataService.GetIdToReliquaryMainPropertyMapAsync(token).ConfigureAwait(false);
|
||||
Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap = await metadataService.GetIdReliquaryAffixMapAsync(token).ConfigureAwait(false);
|
||||
|
||||
List<ReliquaryLevel> reliqueryLevels = await metadataService.GetReliquaryLevelsAsync(token).ConfigureAwait(false);
|
||||
List<MetadataReliquary> reliquaries = await metadataService.GetReliquariesAsync(token).ConfigureAwait(false);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using MetadataAvatar = Snap.Hutao.Model.Metadata.Avatar.Avatar;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
using MetadataWeapon = Snap.Hutao.Model.Metadata.Weapon.Weapon;
|
||||
@@ -18,10 +19,10 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
/// </summary>
|
||||
internal class SummaryFactoryImplementation
|
||||
{
|
||||
private readonly Dictionary<int, MetadataAvatar> idAvatarMap;
|
||||
private readonly Dictionary<int, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<int, MetadataWeapon> idWeaponMap;
|
||||
private readonly Dictionary<int, ReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly Dictionary<AvatarId, MetadataAvatar> idAvatarMap;
|
||||
private readonly Dictionary<WeaponId, MetadataWeapon> idWeaponMap;
|
||||
private readonly Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly List<ReliquaryLevel> reliqueryLevels;
|
||||
private readonly List<MetadataReliquary> reliquaries;
|
||||
|
||||
@@ -35,10 +36,10 @@ internal class SummaryFactoryImplementation
|
||||
/// <param name="reliqueryLevels">圣遗物主属性等级</param>
|
||||
/// <param name="reliquaries">圣遗物</param>
|
||||
public SummaryFactoryImplementation(
|
||||
Dictionary<int, MetadataAvatar> idAvatarMap,
|
||||
Dictionary<int, MetadataWeapon> idWeaponMap,
|
||||
Dictionary<int, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<int, ReliquaryAffix> idReliquaryAffixMap,
|
||||
Dictionary<AvatarId, MetadataAvatar> idAvatarMap,
|
||||
Dictionary<WeaponId, MetadataWeapon> idWeaponMap,
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<ReliquaryAffixId, ReliquaryAffix> idReliquaryAffixMap,
|
||||
List<ReliquaryLevel> reliqueryLevels,
|
||||
List<MetadataReliquary> reliquaries)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using Snap.Hutao.Model.Binding.AvatarProperty;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Converter;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Web.Enka.Model;
|
||||
using System.Runtime.InteropServices;
|
||||
using MetadataReliquary = Snap.Hutao.Model.Metadata.Reliquary.Reliquary;
|
||||
@@ -20,8 +21,8 @@ namespace Snap.Hutao.Service.AvatarInfo.Factory;
|
||||
/// </summary>
|
||||
internal class SummaryReliquaryFactory
|
||||
{
|
||||
private readonly Dictionary<int, MetadataReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly Dictionary<int, FightProperty> idRelicMainPropMap;
|
||||
private readonly Dictionary<ReliquaryAffixId, MetadataReliquaryAffix> idReliquaryAffixMap;
|
||||
private readonly Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap;
|
||||
private readonly List<ReliquaryLevel> reliqueryLevels;
|
||||
private readonly List<MetadataReliquary> reliquaries;
|
||||
|
||||
@@ -38,8 +39,8 @@ internal class SummaryReliquaryFactory
|
||||
/// <param name="avatarInfo">角色信息</param>
|
||||
/// <param name="equip">圣遗物</param>
|
||||
public SummaryReliquaryFactory(
|
||||
Dictionary<int, MetadataReliquaryAffix> idReliquaryAffixMap,
|
||||
Dictionary<int, FightProperty> idRelicMainPropMap,
|
||||
Dictionary<ReliquaryAffixId, MetadataReliquaryAffix> idReliquaryAffixMap,
|
||||
Dictionary<ReliquaryMainAffixId, FightProperty> idRelicMainPropMap,
|
||||
List<ReliquaryLevel> reliqueryLevels,
|
||||
List<MetadataReliquary> reliquaries,
|
||||
ModelAvatarInfo avatarInfo,
|
||||
|
||||
@@ -108,11 +108,8 @@ internal class DailyNoteService : IDailyNoteService, IRecipient<UserRemovedMessa
|
||||
entry.DailyNote = dailyNote;
|
||||
|
||||
// cache
|
||||
Guid userId = entry.UserId;
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
|
||||
// BUG: can found multiple entries sometime
|
||||
entries?.Single(e => e.UserId == userId).UpdateDailyNote(dailyNote);
|
||||
entries?.Single(e => e.UserId == entry.UserId && e.Uid == entry.Uid).UpdateDailyNote(dailyNote);
|
||||
|
||||
if (notify)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
|
||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||
@@ -37,8 +38,8 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
/// <inheritdoc/>
|
||||
public async Task<GachaStatistics> CreateAsync(IEnumerable<GachaItem> items)
|
||||
{
|
||||
Dictionary<int, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
Dictionary<int, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
Dictionary<WeaponId, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, Avatar> nameAvatarMap = await metadataService.GetNameToAvatarMapAsync().ConfigureAwait(false);
|
||||
Dictionary<string, Weapon> nameWeaponMap = await metadataService.GetNameToWeaponMapAsync().ConfigureAwait(false);
|
||||
@@ -66,8 +67,8 @@ internal class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||
private static GachaStatistics CreateCore(
|
||||
IOrderedEnumerable<GachaItem> items,
|
||||
List<HistoryWishBuilder> historyWishBuilders,
|
||||
Dictionary<int, Avatar> avatarMap,
|
||||
Dictionary<int, Weapon> weaponMap,
|
||||
Dictionary<AvatarId, Avatar> avatarMap,
|
||||
Dictionary<WeaponId, Weapon> weaponMap,
|
||||
bool isEmptyHistoryWishVisible)
|
||||
{
|
||||
TypedWishSummaryBuilder permanentWishBuilder = new("奔行世间", TypedWishSummaryBuilder.PermanentWish, 90, 10);
|
||||
|
||||
@@ -13,6 +13,7 @@ using Snap.Hutao.Model.Binding.Gacha.Abstraction;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Model.Metadata.Abstraction;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.GachaLog.Factory;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
@@ -52,8 +53,8 @@ internal class GachaLogService : IGachaLogService, ISupportAsyncInitialization
|
||||
private Dictionary<string, Model.Metadata.Avatar.Avatar>? nameAvatarMap;
|
||||
private Dictionary<string, Model.Metadata.Weapon.Weapon>? nameWeaponMap;
|
||||
|
||||
private Dictionary<int, Model.Metadata.Avatar.Avatar>? idAvatarMap;
|
||||
private Dictionary<int, Model.Metadata.Weapon.Weapon>? idWeaponMap;
|
||||
private Dictionary<AvatarId, Model.Metadata.Avatar.Avatar>? idAvatarMap;
|
||||
private Dictionary<WeaponId, Model.Metadata.Weapon.Weapon>? idWeaponMap;
|
||||
private ObservableCollection<GachaArchive>? archiveCollection;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Binding.Hutao;
|
||||
using Snap.Hutao.Model.Metadata;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
using Snap.Hutao.Service.Metadata;
|
||||
using Snap.Hutao.Web.Hutao.Model;
|
||||
|
||||
@@ -19,7 +20,7 @@ internal class HutaoCache : IHutaoCache
|
||||
private readonly IHutaoService hutaoService;
|
||||
private readonly IMetadataService metadataService;
|
||||
|
||||
private Dictionary<int, Avatar>? idAvatarExtendedMap;
|
||||
private Dictionary<AvatarId, Avatar>? idAvatarExtendedMap;
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的胡桃 API 缓存
|
||||
@@ -55,7 +56,7 @@ internal class HutaoCache : IHutaoCache
|
||||
{
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
Dictionary<int, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
|
||||
Task avatarAppearanceRankTask = AvatarAppearanceRankAsync(idAvatarMap);
|
||||
Task avatarUsageRank = AvatarUsageRanksAsync(idAvatarMap);
|
||||
@@ -82,9 +83,9 @@ internal class HutaoCache : IHutaoCache
|
||||
{
|
||||
if (await metadataService.InitializeAsync().ConfigureAwait(false))
|
||||
{
|
||||
Dictionary<int, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
Dictionary<int, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
Dictionary<int, Model.Metadata.Reliquary.ReliquarySet> idReliquarySetMap = await metadataService.GetEquipAffixIdToReliquarySetMapAsync().ConfigureAwait(false);
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await GetIdAvatarMapExtendedAsync().ConfigureAwait(false);
|
||||
Dictionary<WeaponId, Weapon> idWeaponMap = await metadataService.GetIdToWeaponMapAsync().ConfigureAwait(false);
|
||||
Dictionary<EquipAffixId, Model.Metadata.Reliquary.ReliquarySet> idReliquarySetMap = await metadataService.GetEquipAffixIdToReliquarySetMapAsync().ConfigureAwait(false);
|
||||
|
||||
// AvatarCollocation
|
||||
List<AvatarCollocation> avatarCollocationsRaw = await hutaoService.GetAvatarCollocationsAsync().ConfigureAwait(false);
|
||||
@@ -105,11 +106,11 @@ internal class HutaoCache : IHutaoCache
|
||||
return false;
|
||||
}
|
||||
|
||||
private async ValueTask<Dictionary<int, Avatar>> GetIdAvatarMapExtendedAsync()
|
||||
private async ValueTask<Dictionary<AvatarId, Avatar>> GetIdAvatarMapExtendedAsync()
|
||||
{
|
||||
if (idAvatarExtendedMap == null)
|
||||
{
|
||||
Dictionary<int, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
Dictionary<AvatarId, Avatar> idAvatarMap = await metadataService.GetIdToAvatarMapAsync().ConfigureAwait(false);
|
||||
idAvatarExtendedMap = new(idAvatarMap)
|
||||
{
|
||||
[AvatarIds.PlayerBoy] = new() { Name = "旅行者", Icon = "UI_AvatarIcon_PlayerBoy", Quality = Model.Intrinsic.ItemQuality.QUALITY_ORANGE },
|
||||
@@ -120,7 +121,7 @@ internal class HutaoCache : IHutaoCache
|
||||
return idAvatarExtendedMap;
|
||||
}
|
||||
|
||||
private async Task AvatarAppearanceRankAsync(Dictionary<int, Avatar> idAvatarMap)
|
||||
private async Task AvatarAppearanceRankAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarAppearanceRank> avatarAppearanceRanksRaw = await hutaoService.GetAvatarAppearanceRanksAsync().ConfigureAwait(false);
|
||||
AvatarAppearanceRanks = avatarAppearanceRanksRaw.OrderByDescending(r => r.Floor).Select(rank => new ComplexAvatarRank
|
||||
@@ -130,7 +131,7 @@ internal class HutaoCache : IHutaoCache
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private async Task AvatarUsageRanksAsync(Dictionary<int, Avatar> idAvatarMap)
|
||||
private async Task AvatarUsageRanksAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarUsageRank> avatarUsageRanksRaw = await hutaoService.GetAvatarUsageRanksAsync().ConfigureAwait(false);
|
||||
AvatarUsageRanks = avatarUsageRanksRaw.OrderByDescending(r => r.Floor).Select(rank => new ComplexAvatarRank
|
||||
@@ -140,7 +141,7 @@ internal class HutaoCache : IHutaoCache
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private async Task AvatarConstellationInfosAsync(Dictionary<int, Avatar> idAvatarMap)
|
||||
private async Task AvatarConstellationInfosAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<AvatarConstellationInfo> avatarConstellationInfosRaw = await hutaoService.GetAvatarConstellationInfosAsync().ConfigureAwait(false);
|
||||
AvatarConstellationInfos = avatarConstellationInfosRaw.OrderBy(i => i.HoldingRate).Select(info =>
|
||||
@@ -149,7 +150,7 @@ internal class HutaoCache : IHutaoCache
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
private async Task TeamAppearancesAsync(Dictionary<int, Avatar> idAvatarMap)
|
||||
private async Task TeamAppearancesAsync(Dictionary<AvatarId, Avatar> idAvatarMap)
|
||||
{
|
||||
List<TeamAppearance> teamAppearancesRaw = await hutaoService.GetTeamAppearancesAsync().ConfigureAwait(false);
|
||||
TeamAppearances = teamAppearancesRaw.OrderByDescending(t => t.Floor).Select(team => new ComplexTeamRank(team, idAvatarMap)).ToList();
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
|
||||
namespace Snap.Hutao.Service;
|
||||
@@ -117,6 +118,7 @@ internal class InfoBarService : IInfoBarService
|
||||
Title = title,
|
||||
Message = message,
|
||||
IsOpen = true,
|
||||
Transitions = new() { new AddDeleteThemeTransition() },
|
||||
};
|
||||
|
||||
infoBar.Closed += OnInfoBarClosed;
|
||||
|
||||
@@ -7,6 +7,7 @@ using Snap.Hutao.Model.Metadata.Achievement;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata;
|
||||
|
||||
@@ -47,7 +48,7 @@ internal interface IMetadataService
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>装备被动Id到圣遗物套装的映射</returns>
|
||||
ValueTask<Dictionary<int, ReliquarySet>> GetEquipAffixIdToReliquarySetMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<EquipAffixId, ReliquarySet>> GetEquipAffixIdToReliquarySetMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取卡池配置列表
|
||||
@@ -61,28 +62,28 @@ internal interface IMetadataService
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Id到角色的字典</returns>
|
||||
ValueTask<Dictionary<int, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<AvatarId, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取ID到圣遗物副词条的字典
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>字典</returns>
|
||||
ValueTask<Dictionary<int, ReliquaryAffix>> GetIdReliquaryAffixMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<ReliquaryAffixId, ReliquaryAffix>> GetIdReliquaryAffixMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取圣遗物主词条Id与属性的字典
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>字典</returns>
|
||||
ValueTask<Dictionary<int, FightProperty>> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<ReliquaryMainAffixId, FightProperty>> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取ID到武器的字典
|
||||
/// </summary>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>Id到武器的字典</returns>
|
||||
ValueTask<Dictionary<int, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default);
|
||||
ValueTask<Dictionary<WeaponId, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取名称到角色的字典
|
||||
|
||||
@@ -5,6 +5,7 @@ using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Metadata.Avatar;
|
||||
using Snap.Hutao.Model.Metadata.Reliquary;
|
||||
using Snap.Hutao.Model.Metadata.Weapon;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Service.Metadata;
|
||||
|
||||
@@ -14,33 +15,33 @@ namespace Snap.Hutao.Service.Metadata;
|
||||
internal partial class MetadataService
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, ReliquarySet>> GetEquipAffixIdToReliquarySetMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<EquipAffixId, ReliquarySet>> GetEquipAffixIdToReliquarySetMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, ReliquarySet>("ReliquarySet", r => r.EquipAffixId, token);
|
||||
return FromCacheAsDictionaryAsync<EquipAffixId, ReliquarySet>("ReliquarySet", r => r.EquipAffixId, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<AvatarId, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, Avatar>("Avatar", a => a.Id, token);
|
||||
return FromCacheAsDictionaryAsync<AvatarId, Avatar>("Avatar", a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, ReliquaryAffix>> GetIdReliquaryAffixMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<ReliquaryAffixId, ReliquaryAffix>> GetIdReliquaryAffixMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, ReliquaryAffix>("ReliquaryAffix", a => a.Id, token);
|
||||
return FromCacheAsDictionaryAsync<ReliquaryAffixId, ReliquaryAffix>("ReliquaryAffix", a => a.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, FightProperty>> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<ReliquaryMainAffixId, FightProperty>> GetIdToReliquaryMainPropertyMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, FightProperty, ReliquaryAffixBase>("ReliquaryMainAffix", r => r.Id, r => r.Type, token);
|
||||
return FromCacheAsDictionaryAsync<ReliquaryMainAffixId, FightProperty, ReliquaryAffixBase>("ReliquaryMainAffix", r => r.Id, r => r.Type, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ValueTask<Dictionary<int, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default)
|
||||
public ValueTask<Dictionary<WeaponId, Weapon>> GetIdToWeaponMapAsync(CancellationToken token = default)
|
||||
{
|
||||
return FromCacheAsDictionaryAsync<int, Weapon>("Weapon", w => w.Id, token);
|
||||
return FromCacheAsDictionaryAsync<WeaponId, Weapon>("Weapon", w => w.Id, token);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
<None Remove="View\Dialog\GachaLogUrlDialog.xaml" />
|
||||
<None Remove="View\Dialog\GameAccountNameDialog.xaml" />
|
||||
<None Remove="View\Dialog\LoginMihoyoBBSDialog.xaml" />
|
||||
<None Remove="View\Dialog\SignInWebViewDialog.xaml" />
|
||||
<None Remove="View\Dialog\UserDialog.xaml" />
|
||||
<None Remove="View\MainView.xaml" />
|
||||
<None Remove="View\Page\AchievementPage.xaml" />
|
||||
@@ -145,7 +146,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25231-preview" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.25247-preview" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1" />
|
||||
<PackageReference Include="StyleCop.Analyzers.Unstable" Version="1.2.0.435">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
@@ -169,6 +170,11 @@
|
||||
<ItemGroup>
|
||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\SignInWebViewDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="View\Dialog\LoginMihoyoBBSDialog.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<ContentDialog
|
||||
x:Class="Snap.Hutao.View.Dialog.SignInWebViewDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Style="{StaticResource DefaultContentDialogStyle}">
|
||||
|
||||
<Grid Loaded="OnGridLoaded" Height="600" Width="380">
|
||||
<WebView2 Name="WebView"/>
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License. See LICENSE in the project root for license information.
|
||||
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Model.Binding.User;
|
||||
using Snap.Hutao.Service.Abstraction;
|
||||
using Snap.Hutao.Service.User;
|
||||
using Snap.Hutao.Web.Bridge;
|
||||
using Snap.Hutao.Web.Bridge.Model;
|
||||
using Snap.Hutao.Web.Bridge.Model.Event;
|
||||
using Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
using Windows.UI.Popups;
|
||||
|
||||
namespace Snap.Hutao.View.Dialog;
|
||||
|
||||
/// <summary>
|
||||
/// ǩ<><C7A9><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3>ͼ<EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
|
||||
/// </summary>
|
||||
public sealed partial class SignInWebViewDialog : ContentDialog
|
||||
{
|
||||
/// <summary>
|
||||
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µ<EFBFBD>ǩ<EFBFBD><C7A9><EFBFBD><EFBFBD>ҳ<EFBFBD><D2B3>ͼ<EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
|
||||
/// </summary>
|
||||
/// <param name="window"><3E><><EFBFBD><EFBFBD></param>
|
||||
public SignInWebViewDialog(MainWindow window)
|
||||
{
|
||||
InitializeComponent();
|
||||
XamlRoot = window.Content.XamlRoot;
|
||||
}
|
||||
|
||||
private void OnGridLoaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
InitializeAsync().SafeForget();
|
||||
}
|
||||
|
||||
private async Task InitializeAsync()
|
||||
{
|
||||
await WebView.EnsureCoreWebView2Async();
|
||||
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
|
||||
IUserService userService = Ioc.Default.GetRequiredService<IUserService>();
|
||||
IInfoBarService infoBarService = Ioc.Default.GetRequiredService<IInfoBarService>();
|
||||
ILogger<MiHoYoJsBridge> logger = Ioc.Default.GetRequiredService<ILogger<MiHoYoJsBridge>>();
|
||||
User? user = userService.Current;
|
||||
|
||||
coreWebView2.SetCookie(user?.CookieToken, user?.Ltoken);
|
||||
coreWebView2.SetMobileUserAgent();
|
||||
coreWebView2.InitializeBridge(logger, false)
|
||||
.Register<JsEventClosePage>(e => Hide())
|
||||
.Register<JsEventRealPersonValidation>(e => infoBarService.Information("<22><EFBFBD>ʹ<EFBFBD>ô˹<C3B4><CBB9><EFBFBD>", "<22><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʵ<EFBFBD><CAB5><EFBFBD><EFBFBD>֤<EFBFBD><D6A4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>"))
|
||||
.Register<JsEventGetStatusBarHeight>(s => s.Callback(result => result.Data["statusBarHeight"] = 0))
|
||||
.Register<JsEventGetDynamicSecretV1>(s => s.Callback(result =>
|
||||
{
|
||||
result.Data["DS"] = DynamicSecretHandler.GetDynamicSecret(nameof(SaltType.K2), nameof(DynamicSecretVersion.Gen1), includeChars: true);
|
||||
}))
|
||||
.Register<JsEventGetUserInfo>(s => s.Callback(result =>
|
||||
{
|
||||
result.Data["id"] = "111";
|
||||
result.Data["gender"] = 0;
|
||||
result.Data["nickname"] = "222";
|
||||
result.Data["introduce"] = "333";
|
||||
result.Data["avatar_url"] = "https://img-static.mihoyo.com/communityweb/upload/52de23f1b1a060e4ccaa8b24c1305dd9.png";
|
||||
}));
|
||||
|
||||
coreWebView2.OpenDevToolsWindow();
|
||||
coreWebView2.Navigate("https://webstatic.mihoyo.com/bbs/event/signin-ys/index.html?act_id=e202009291139501");
|
||||
}
|
||||
}
|
||||
@@ -133,11 +133,6 @@
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
</ResourceDictionary>
|
||||
</StackPanel.Resources>
|
||||
<StackPanel.Transitions>
|
||||
<TransitionCollection>
|
||||
<AddDeleteThemeTransition/>
|
||||
</TransitionCollection>
|
||||
</StackPanel.Transitions>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -194,6 +194,13 @@
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
<TextBlock
|
||||
Margin="10,6,0,6"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
Text="Webview"/>
|
||||
<CommandBar DefaultLabelPosition="Right">
|
||||
<AppBarButton Label="米游社签到" Icon="{shcm:FontIcon Glyph=}" Command="{Binding ShowSignInWebViewDialogCommand}"/>
|
||||
</CommandBar>
|
||||
<TextBlock
|
||||
Margin="10,6,0,6"
|
||||
Style="{StaticResource BaseTextBlockStyle}"
|
||||
|
||||
@@ -46,6 +46,7 @@ internal class UserViewModel : ObservableObject
|
||||
LoginMihoyoUserCommand = new RelayCommand(LoginMihoyoUser);
|
||||
RemoveUserCommand = asyncRelayCommandFactory.Create<User>(RemoveUserAsync);
|
||||
CopyCookieCommand = new RelayCommand<User>(CopyCookie);
|
||||
ShowSignInWebViewDialogCommand = asyncRelayCommandFactory.Create(ShowSignInWebViewDialogAsync);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,6 +84,8 @@ internal class UserViewModel : ObservableObject
|
||||
/// </summary>
|
||||
public ICommand LoginMihoyoUserCommand { get; }
|
||||
|
||||
public ICommand ShowSignInWebViewDialogCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 移除用户命令
|
||||
/// </summary>
|
||||
@@ -165,4 +168,10 @@ internal class UserViewModel : ObservableObject
|
||||
infoBarService.Error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ShowSignInWebViewDialogAsync()
|
||||
{
|
||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||
await new SignInWebViewDialog(mainWindow).ShowAsync();
|
||||
}
|
||||
}
|
||||
@@ -226,7 +226,7 @@ internal static class ApiEndpoints
|
||||
/// <summary>
|
||||
/// 获取V2Stoken
|
||||
/// </summary>
|
||||
public const string AccountGetSTokenByOldtoken = $"{PassportApi}/account/ma-cn-session/app/getTokenBySToken";
|
||||
public const string AccountGetSTokenByOldToken = $"{PassportApi}/account/ma-cn-session/app/getTokenBySToken";
|
||||
|
||||
/// <summary>
|
||||
/// 登录
|
||||
|
||||
107
src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeExtension.cs
Normal file
107
src/Snap.Hutao/Snap.Hutao/Web/Bridge/BridgeExtension.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Web.Hoyolab;
|
||||
using WinRT;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// Bridge 拓展
|
||||
/// </summary>
|
||||
public static class BridgeExtension
|
||||
{
|
||||
private const string InitializeJsInterfaceScript = """
|
||||
let c = {};
|
||||
c.postMessage = str => chrome.webview.hostObjects.MiHoYoJsBridge.OnMessage(str);
|
||||
c.closePage = () => c.postMessage('{"method":"closePage"}');
|
||||
window.MiHoYoJSInterface = c;
|
||||
""";
|
||||
|
||||
private const string HideScrollBarScript = """
|
||||
let st = document.createElement('style');
|
||||
st.innerHTML = '::-webkit-scrollbar{display:none}';
|
||||
document.querySelector('body').appendChild(st);
|
||||
""";
|
||||
|
||||
/// <summary>
|
||||
/// 设置 移动端UA
|
||||
/// </summary>
|
||||
/// <param name="webView">webview2</param>
|
||||
public static void SetMobileUserAgent(this CoreWebView2 webView)
|
||||
{
|
||||
webView.Settings.UserAgent = "Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/106.0.5249.126 Mobile Safari/537.36 miHoYoBBS/2.41.0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化调用桥
|
||||
/// </summary>
|
||||
/// <param name="webView">webview2</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
/// <param name="checkHost">检查主机</param>
|
||||
/// <returns>初始化后的调用桥</returns>
|
||||
public static MiHoYoJsBridge InitializeBridge(this CoreWebView2 webView, ILogger<MiHoYoJsBridge> logger, bool checkHost = true)
|
||||
{
|
||||
MiHoYoJsBridge bridge = new(webView, logger);
|
||||
var result = webView.As<ICoreWebView2Interop>().AddHostObjectToScript("MiHoYoJsBridge", bridge);
|
||||
|
||||
webView.DOMContentLoaded += OnDOMContentLoaded;
|
||||
webView.NavigationStarting += (coreWebView2, args) => OnWebViewNavigationStarting(coreWebView2, args, checkHost);
|
||||
|
||||
return bridge;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置WebView2的Cookie
|
||||
/// </summary>
|
||||
/// <param name="webView">webview2</param>
|
||||
/// <param name="cookieToken">CookieToken</param>
|
||||
/// <param name="ltoken">Ltoken</param>
|
||||
/// <param name="stoken">Stoken</param>
|
||||
/// <returns>链式调用的WebView2</returns>
|
||||
public static CoreWebView2 SetCookie(this CoreWebView2 webView, Cookie? cookieToken = null, Cookie? ltoken = null, Cookie? stoken = null)
|
||||
{
|
||||
CoreWebView2CookieManager cookieManager = webView.CookieManager;
|
||||
|
||||
if (cookieToken != null)
|
||||
{
|
||||
cookieManager.AddMihoyoCookie("account_id", cookieToken).AddMihoyoCookie("cookie_token", cookieToken);
|
||||
}
|
||||
|
||||
if (ltoken != null)
|
||||
{
|
||||
cookieManager.AddMihoyoCookie("ltuid", ltoken).AddMihoyoCookie("ltoken", ltoken);
|
||||
}
|
||||
|
||||
if (stoken != null)
|
||||
{
|
||||
cookieManager.AddMihoyoCookie("stuid", stoken).AddMihoyoCookie("stoken", stoken);
|
||||
}
|
||||
|
||||
return webView;
|
||||
}
|
||||
|
||||
private static CoreWebView2CookieManager AddMihoyoCookie(this CoreWebView2CookieManager manager, string name, Cookie cookie)
|
||||
{
|
||||
manager.AddOrUpdateCookie(manager.CreateCookie(name, cookie[name], ".mihoyo.com", "/"));
|
||||
return manager;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "VSTHRD100")]
|
||||
private static async void OnDOMContentLoaded(CoreWebView2 coreWebView2, CoreWebView2DOMContentLoadedEventArgs args)
|
||||
{
|
||||
string result = await coreWebView2.ExecuteScriptAsync(HideScrollBarScript);
|
||||
_ = result;
|
||||
}
|
||||
|
||||
[SuppressMessage("", "VSTHRD100")]
|
||||
private static async void OnWebViewNavigationStarting(CoreWebView2 coreWebView2, CoreWebView2NavigationStartingEventArgs args, bool checkHost)
|
||||
{
|
||||
if (!checkHost || new Uri(args.Uri).Host.EndsWith("mihoyo.com"))
|
||||
{
|
||||
string result = await coreWebView2.ExecuteScriptAsync(InitializeJsInterfaceScript);
|
||||
_ = result;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/Snap.Hutao/Snap.Hutao/Web/Bridge/ICoreWebView2Interop.cs
Normal file
24
src/Snap.Hutao/Snap.Hutao/Web/Bridge/ICoreWebView2Interop.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using Windows.Win32.Foundation;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// ICoreWebView2Interop
|
||||
/// </summary>
|
||||
[ComImport]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
[Guid("912b34a7-d10b-49c4-af18-7cb7e604e01a")]
|
||||
public interface ICoreWebView2Interop
|
||||
{
|
||||
/// <summary>
|
||||
/// Add the provided host object to script running in the WebView with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="name">名称</param>
|
||||
/// <param name="obj">对象</param>
|
||||
/// <returns>结果</returns>
|
||||
HRESULT AddHostObjectToScript([In] string name, [In] ref object obj);
|
||||
}
|
||||
20
src/Snap.Hutao/Snap.Hutao/Web/Bridge/IMiHoYoJsBridge.cs
Normal file
20
src/Snap.Hutao/Snap.Hutao/Web/Bridge/IMiHoYoJsBridge.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// 调用桥暴露的COM接口
|
||||
/// </summary>
|
||||
[ComVisible(true)]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
|
||||
public interface IMiHoYoJsBridge
|
||||
{
|
||||
/// <summary>
|
||||
/// 消息发生时调用
|
||||
/// </summary>
|
||||
/// <param name="str">消息</param>
|
||||
void OnMessage(string str);
|
||||
}
|
||||
103
src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJsBridge.cs
Normal file
103
src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJsBridge.cs
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Microsoft.Web.WebView2.Core;
|
||||
using Snap.Hutao.Web.Bridge.Model;
|
||||
using Snap.Hutao.Web.Bridge.Model.Event;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge;
|
||||
|
||||
/// <summary>
|
||||
/// 调用桥
|
||||
/// </summary>
|
||||
[ComVisible(true)]
|
||||
[ClassInterface(ClassInterfaceType.AutoDual)]
|
||||
public sealed class MiHoYoJsBridge /*: IMiHoYoJsBridge*/
|
||||
{
|
||||
private readonly CoreWebView2 webView;
|
||||
private readonly ILogger<MiHoYoJsBridge>? logger;
|
||||
|
||||
private readonly Dictionary<string, string> jsWebInvokeTypeCache = new();
|
||||
private readonly Dictionary<string, Action<JsParam>> callbackHandlers = new();
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的调用桥
|
||||
/// </summary>
|
||||
/// <param name="webView">webview2</param>
|
||||
/// <param name="logger">日志器</param>
|
||||
internal MiHoYoJsBridge(CoreWebView2 webView, ILogger<MiHoYoJsBridge>? logger = null)
|
||||
{
|
||||
this.webView = webView;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息发生时调用
|
||||
/// </summary>
|
||||
/// <param name="message">消息</param>
|
||||
public void OnMessage(string message)
|
||||
{
|
||||
logger?.LogInformation("[OnMessage] {message}", message);
|
||||
|
||||
JsParam p = JsonSerializer.Deserialize<JsParam>(message)!;
|
||||
p.Bridge = this;
|
||||
|
||||
callbackHandlers.GetValueOrDefault(p.Method)?.Invoke(p);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用JS回调
|
||||
/// </summary>
|
||||
/// <param name="callbackName">回调名称</param>
|
||||
/// <param name="payload">传输的数据</param>
|
||||
/// <returns>执行结果</returns>
|
||||
public Task<string> InvokeJsCallbackAsync(string callbackName, string? payload = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(callbackName))
|
||||
{
|
||||
return Task.FromResult(string.Empty);
|
||||
}
|
||||
|
||||
string dataStr = payload == null ? string.Empty : $", {payload}";
|
||||
string js = $"javascript:mhyWebBridge(\"{callbackName}\"{dataStr})";
|
||||
logger?.LogInformation("[InvokeJsCallback] {js}", js);
|
||||
return webView.ExecuteScriptAsync(js).AsTask();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册回调
|
||||
/// </summary>
|
||||
/// <typeparam name="T">回调类型</typeparam>
|
||||
/// <param name="callback">回调</param>
|
||||
/// <returns>桥</returns>
|
||||
public MiHoYoJsBridge Register<T>(Action<JsParam> callback)
|
||||
where T : notnull
|
||||
{
|
||||
callbackHandlers[GetCallbackName<T>()] = callback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 注册回调
|
||||
/// </summary>
|
||||
/// <typeparam name="T">回调类型</typeparam>
|
||||
/// <param name="callback">回调</param>
|
||||
/// <returns>桥</returns>
|
||||
public MiHoYoJsBridge Register<T>(Action<JsParam, T> callback)
|
||||
where T : notnull
|
||||
{
|
||||
callbackHandlers[GetCallbackName<T>()] = p => callback(p, p.Data.As<T>());
|
||||
return this;
|
||||
}
|
||||
|
||||
private string GetCallbackName<T>()
|
||||
{
|
||||
Type type = typeof(T);
|
||||
string invokeName = type.GetCustomAttribute<WebInvokeAttribute>()?.Name
|
||||
?? throw new ArgumentException("Type Callback not registered.");
|
||||
|
||||
return jsWebInvokeTypeCache[type.Name] = invokeName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge.Model.Event;
|
||||
|
||||
/// <summary>
|
||||
/// Web 调用
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
|
||||
public class WebInvokeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的Web 调用特性
|
||||
/// </summary>
|
||||
/// <param name="name">函数名称</param>
|
||||
public WebInvokeAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用函数名称
|
||||
/// </summary>
|
||||
public string Name { get; init; }
|
||||
}
|
||||
|
||||
public class ButtonParam
|
||||
{
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("style")]
|
||||
public string Style { get; set; } = default!;
|
||||
}
|
||||
|
||||
public abstract class GenAuthKeyBase
|
||||
{
|
||||
[JsonPropertyName("game_biz")]
|
||||
public string Biz { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("auth_appid")]
|
||||
public string AppId { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("game_uid")]
|
||||
public uint Uid { get; set; }
|
||||
|
||||
[JsonPropertyName("region")]
|
||||
public string Region { get; set; } = default!;
|
||||
}
|
||||
|
||||
[WebInvoke("closePage")]
|
||||
public struct JsEventClosePage
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("configure_share")]
|
||||
public class JsEventConfigureShare
|
||||
{
|
||||
[JsonPropertyName("enable")]
|
||||
public bool Enable { get; set; }
|
||||
}
|
||||
|
||||
[WebInvoke("genAppAuthKey")]
|
||||
public class JsEventGenAppAuthKey
|
||||
: GenAuthKeyBase
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("genAuthKey")]
|
||||
public class JsEventGenAuthKey
|
||||
: GenAuthKeyBase
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("getActionTicket")]
|
||||
public class JsEventGetActionTicket
|
||||
{
|
||||
[JsonPropertyName("action_type")]
|
||||
public string ActionType { get; set; } = default!;
|
||||
}
|
||||
|
||||
[WebInvoke("getCookieToken")]
|
||||
public class JsEventGetCookieToken
|
||||
{
|
||||
[JsonPropertyName("forceRefresh")]
|
||||
public bool ForceRefresh { get; set; }
|
||||
}
|
||||
|
||||
[WebInvoke("getDS")]
|
||||
public struct JsEventGetDynamicSecretV1
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("getDS2")]
|
||||
public class JsEventGetDynamicSecretV2
|
||||
{
|
||||
[JsonPropertyName("query")]
|
||||
public Dictionary<string, string> Query { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("body")]
|
||||
public string Body { get; set; } = default!;
|
||||
}
|
||||
|
||||
[WebInvoke("getNotificationSettings")]
|
||||
public struct JsEventGetNotificationSettings
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("startRealnameAuth")]
|
||||
public struct JsEventGetRealNameStatus
|
||||
{
|
||||
// guess
|
||||
}
|
||||
|
||||
[WebInvoke("getHTTPRequestHeaders")]
|
||||
public struct JsEventGetRequestHeader
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("getStatusBarHeight")]
|
||||
public struct JsEventGetStatusBarHeight
|
||||
{
|
||||
// just zero
|
||||
}
|
||||
|
||||
[WebInvoke("getUserInfo")]
|
||||
public struct JsEventGetUserInfo
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("getCookieInfo")]
|
||||
public struct JsEventGetWebLoginInfo
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("openSystemBrowser")]
|
||||
public class JsEventOpenSystemBrowser
|
||||
{
|
||||
[JsonPropertyName("open_url")]
|
||||
public string PageUrl { get; set; } = default!;
|
||||
}
|
||||
|
||||
[WebInvoke("pushPage")]
|
||||
public class JsEventPushPage
|
||||
{
|
||||
private string pageUrl = default!;
|
||||
|
||||
[JsonPropertyName("page")]
|
||||
public string PageUrl
|
||||
{
|
||||
get => pageUrl;
|
||||
set => SetPageUrl(value);
|
||||
}
|
||||
|
||||
private void SetPageUrl(string value)
|
||||
{
|
||||
pageUrl = value.StartsWith("mihoyobbs")
|
||||
? value.Replace("mihoyobbs://", "https://bbs.mihoyo.com/dby/").Replace("topic", "topicDetail")
|
||||
: value;
|
||||
}
|
||||
}
|
||||
|
||||
[WebInvoke("startRealPersonValidation")]
|
||||
public struct JsEventRealPersonValidation
|
||||
{
|
||||
}
|
||||
|
||||
[WebInvoke("saveLoginTicket")]
|
||||
public class JsEventSaveLoginTicket
|
||||
{
|
||||
[JsonPropertyName("login_ticket")]
|
||||
public string LoginTicket { get; set; } = default!;
|
||||
}
|
||||
|
||||
[WebInvoke("showAlertDialog")]
|
||||
public class JsEventShowAlertDialog
|
||||
{
|
||||
[JsonPropertyName("title")]
|
||||
public string Title { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("buttons")]
|
||||
public List<ButtonParam> Buttons { get; set; } = new();
|
||||
}
|
||||
|
||||
[WebInvoke("showToast")]
|
||||
public class JsEventShowToast
|
||||
{
|
||||
[JsonPropertyName("toast")]
|
||||
public string Text { get; set; } = default!;
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; } = default!;
|
||||
}
|
||||
60
src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
Normal file
60
src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
namespace Snap.Hutao.Web.Bridge.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Js 参数
|
||||
/// </summary>
|
||||
public class JsParam
|
||||
{
|
||||
/// <summary>
|
||||
/// 方法名称
|
||||
/// </summary>
|
||||
[JsonPropertyName("method")]
|
||||
public string Method { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
[JsonPropertyName("payload")]
|
||||
public JsonElement Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 回调名称
|
||||
/// </summary>
|
||||
[JsonPropertyName("callback")]
|
||||
public string CallbackName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 对应的调用桥
|
||||
/// </summary>
|
||||
internal MiHoYoJsBridge Bridge { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// 执行回调
|
||||
/// </summary>
|
||||
/// <param name="resultFactory">结果工厂</param>
|
||||
public void Callback(Func<JsResult>? resultFactory = null)
|
||||
{
|
||||
JsResult? result = resultFactory?.Invoke() ?? new();
|
||||
Callback(result?.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行回调
|
||||
/// </summary>
|
||||
/// <param name="resultModifier">结果工厂</param>
|
||||
public void Callback(Action<JsResult> resultModifier)
|
||||
{
|
||||
JsResult result = new();
|
||||
resultModifier(result);
|
||||
Callback(result?.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行回调
|
||||
/// </summary>
|
||||
/// <param name="result">结果</param>
|
||||
public void Callback(string? result = null)
|
||||
{
|
||||
Bridge.InvokeJsCallbackAsync(CallbackName, result).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
37
src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsResult.cs
Normal file
37
src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsResult.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Js结果
|
||||
/// </summary>
|
||||
public class JsResult
|
||||
{
|
||||
/// <summary>
|
||||
/// 代码
|
||||
/// </summary>
|
||||
[JsonPropertyName("retcode")]
|
||||
public int Code { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 消息
|
||||
/// </summary>
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
[JsonPropertyName("data")]
|
||||
public Dictionary<string, object> Data { get; set; } = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return JsonSerializer.Serialize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Snap.Hutao.Web.Bridge.Model;
|
||||
|
||||
/// <summary>
|
||||
/// JsonElement 拓展
|
||||
/// </summary>
|
||||
public static class JsonElementExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 序列化到对应类型
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对应类型</typeparam>
|
||||
/// <param name="jsonElement">元素</param>
|
||||
/// <returns>对应类型的实例</returns>
|
||||
public static T As<T>(this JsonElement jsonElement)
|
||||
where T : notnull
|
||||
{
|
||||
return jsonElement.Deserialize<T>()!;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Web.Hoyolab.DynamicSecret;
|
||||
[Injection(InjectAs.Transient)]
|
||||
public class DynamicSecretHandler : DelegatingHandler
|
||||
{
|
||||
private const string RandomRange = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
private const string RandomRange = "abcdefghijklmnopqrstuvwxyz1234567890";
|
||||
|
||||
// https://github.com/UIGF-org/Hoyolab.Salt
|
||||
private static readonly ImmutableDictionary<string, string> DynamicSecrets = new Dictionary<string, string>()
|
||||
@@ -66,6 +66,36 @@ public class DynamicSecretHandler : DelegatingHandler
|
||||
return await base.SendAsync(request, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取DS
|
||||
/// </summary>
|
||||
/// <param name="saltType">nameof <see cref="SaltType"/></param>
|
||||
/// <param name="version">nameof <see cref="DynamicSecretVersion"/></param>
|
||||
/// <param name="body">body</param>
|
||||
/// <param name="query">query</param>
|
||||
/// <param name="includeChars">是否需要字母</param>
|
||||
/// <returns>DS</returns>
|
||||
public static string GetDynamicSecret(string saltType, string version, string? body = null, string? query = null, bool includeChars = true)
|
||||
{
|
||||
string salt = DynamicSecrets[saltType];
|
||||
|
||||
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
|
||||
string r = includeChars ? GetRandomStringWithChars() : GetRandomStringNoChars();
|
||||
|
||||
string dsContent = $"salt={salt}&t={t}&r={r}";
|
||||
|
||||
if (version == nameof(DynamicSecretVersion.Gen2))
|
||||
{
|
||||
string[] queries = Uri.UnescapeDataString(query!).Split('?', 2);
|
||||
string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty;
|
||||
|
||||
dsContent = $"{dsContent}&b={body}&q={q}";
|
||||
}
|
||||
|
||||
return Md5Convert.ToHexString(dsContent).ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static string GetRandomStringWithChars()
|
||||
{
|
||||
StringBuilder sb = new(6);
|
||||
|
||||
@@ -69,7 +69,7 @@ internal class PassportClient2
|
||||
HttpResponseMessage message = await httpClient
|
||||
.SetHeader("Cookie", stokenV1.ToString())
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.PROD, true)
|
||||
.PostAsync(ApiEndpoints.AccountGetSTokenByOldtoken, null, token)
|
||||
.PostAsync(ApiEndpoints.AccountGetSTokenByOldToken, null, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
Response<LoginResult>? resp = await message.Content
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
|
||||
namespace Snap.Hutao.Web.Hutao.Model;
|
||||
|
||||
/// <summary>
|
||||
@@ -23,7 +25,7 @@ public class ReliquarySet
|
||||
/// <summary>
|
||||
/// Id
|
||||
/// </summary>
|
||||
public int EquipAffixId { get; }
|
||||
public ExtendedEquipAffixId EquipAffixId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 个数
|
||||
|
||||
Reference in New Issue
Block a user