diff --git a/README.md b/README.md
index d9cf7fe5c3..2bad66cc80 100644
--- a/README.md
+++ b/README.md
@@ -1,35 +1,26 @@
-# Snap.Hutao
+# [Snap.Hutao](https://hut.ao)
> 唷,找本堂主有何贵干呀?
![Snap.Hutao](https://repobeats.axiom.co/api/embed/f029553fbe0c60689b1710476ec8512452163fc9.svg)
-## 项目首页(文档)
+# 特别感谢
-[![Deploy Docs](https://github.com/DGP-Studio/Snap.Hutao.Docs/actions/workflows/deploy-docs.yml/badge.svg)](https://github.com/DGP-Studio/Snap.Hutao.Docs/actions/workflows/deploy-docs.yml)
+### 原神组织与个人
-[HUT.AO](https://hut.ao)
+* [HolographicHat](https://github.com/HolographicHat)
+* [UIGF organization](https://uigf.org)
-## 安装
-
-* 前往 [下载页面](https://go.hut.ao/down) 下载最新版本的 `胡桃` 安装包
-* (曾启用的可以跳过此步骤)在系统设置中打开 **开发者选项** 界面,勾选 `开发人员模式` 和 `允许 PowerShell 脚本`
-* 完全解压后,右键使用 powershell 运行 `install.ps1` 文件
-* 安装完成后可以关闭 `允许 PowerShell 脚本`
-
-## 特别感谢
-
-### 原神项目
+### 特定的原神项目
* [biuuu/genshin-wish-export](https://github.com/biuuu/genshin-wish-export)
-* [HolographicHat/YaeAchievement](https://github.com/HolographicHat/YaeAchievement)
-* [HolographicHat/MiHoYoWebBridge](https://github.com/HolographicHat/MiHoYoWebBridge)
* [xunkong/xunkong](https://github.com/xunkong/xunkong)
* [YuehaiTeam/cocogoat](https://github.com/YuehaiTeam/cocogoat)
-### 技术栈
+### 使用的技术栈
* [CommunityToolkit/dotnet](https://github.com/CommunityToolkit/dotnet)
* [CommunityToolkit/WindowsCommunityToolkit](https://github.com/CommunityToolkit/WindowsCommunityToolkit)
+* [dahall/taskscheduler](https://github.com/dahall/taskscheduler)
* [dotnet/efcore](https://github.com/dotnet/efcore)
* [dotnet/runtime](https://github.com/dotnet/runtime)
* [DotNetAnalyzers/StyleCopAnalyzers](https://github.com/DotNetAnalyzers/StyleCopAnalyzers)
diff --git a/src/Snap.Hutao/Snap.Hutao/Factory/PickerFactory.cs b/src/Snap.Hutao/Snap.Hutao/Factory/PickerFactory.cs
index b44da7bdbe..5aff475177 100644
--- a/src/Snap.Hutao/Snap.Hutao/Factory/PickerFactory.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Factory/PickerFactory.cs
@@ -3,6 +3,9 @@
using Snap.Hutao.Factory.Abstraction;
using Windows.Storage.Pickers;
+using Windows.Win32;
+using Windows.Win32.UI.Shell;
+using WinRT;
using WinRT.Interop;
namespace Snap.Hutao.Factory;
@@ -37,6 +40,7 @@ public FileOpenPicker GetFileOpenPicker(PickerLocationId location, string commit
picker.FileTypeFilter.Add(type);
}
+ // https://github.com/microsoft/WindowsAppSDK/issues/2931
picker.FileTypeFilter.Add(AnyType);
return picker;
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs
index 758f7d7e01..74d56fca93 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItem.cs
@@ -25,6 +25,7 @@ public CultivateItem(Material inner, Entity.CultivateItem entity)
Inner = inner;
Entity = entity;
isFinished = Entity.IsFinished;
+ IsToday = CultivateItemHelper.IsTodaysMaterial(inner.Id, DateTimeOffset.Now);
FinishStateCommand = new RelayCommand(FlipIsFinished);
}
@@ -59,6 +60,11 @@ public bool IsFinished
}
}
+ ///
+ /// 是否为今日物品
+ ///
+ public bool IsToday { get; }
+
private void FlipIsFinished()
{
IsFinished = !IsFinished;
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs
new file mode 100644
index 0000000000..775e4563b9
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Cultivation/CultivateItemHelper.cs
@@ -0,0 +1,64 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Binding.Cultivation;
+
+///
+/// 养成物品帮助类
+///
+public static class CultivateItemHelper
+{
+ ///
+ /// 判断是否为当日物品
+ ///
+ /// 材料Id
+ /// 时间
+ /// 是否为当日物品
+ public static bool IsTodaysMaterial(int itemId, DateTimeOffset now)
+ {
+ DateTimeOffset utcNow = now.ToUniversalTime();
+ utcNow = utcNow.AddHours(4);
+ DayOfWeek dayOfWeek = utcNow.DayOfWeek;
+
+ return dayOfWeek switch
+ {
+ DayOfWeek.Monday or DayOfWeek.Thursday => itemId switch
+ {
+ 104301 or 104302 or 104303 => true, // 「自由」
+ 104310 or 104311 or 104312 => true, // 「繁荣」
+ 104320 or 104321 or 104322 => true, // 「浮世」
+ 104329 or 104330 or 104331 => true, // 「诤言」
+ 114001 or 114002 or 114003 or 114004 => true, // 高塔孤王
+ 114013 or 114014 or 114015 or 114016 => true, // 孤云寒林
+ 114025 or 114026 or 114027 or 114028 => true, // 远海夷地
+ 114037 or 114038 or 114039 or 114040 => true, // 谧林涓露
+ _ => false,
+ },
+ DayOfWeek.Tuesday or DayOfWeek.Friday => itemId switch
+ {
+ 104304 or 104305 or 104306 => true, // 「抗争」
+ 104313 or 104314 or 104315 => true, // 「勤劳」
+ 104323 or 104324 or 104325 => true, // 「风雅」
+ 104332 or 104333 or 104334 => true, // 「巧思」
+ 114005 or 114006 or 114007 or 114008 => true, // 凛风奔狼
+ 114017 or 114018 or 114019 or 114020 => true, // 雾海云间
+ 114029 or 114030 or 114031 or 114032 => true, // 鸣神御灵
+ 114041 or 114042 or 114043 or 114044 => true, // 绿洲花园
+ _ => false,
+ },
+ DayOfWeek.Wednesday or DayOfWeek.Saturday => itemId switch
+ {
+ 104307 or 104308 or 104309 => true, // 「诗文」
+ 104316 or 104317 or 104318 => true, // 「黄金」
+ 104326 or 104327 or 104328 => true, // 「天光」
+ 104335 or 104336 or 104337 => true, // 「笃行」
+ 114009 or 114010 or 114011 or 114012 => true, // 狮牙斗士
+ 114021 or 114022 or 114023 or 114024 => true, // 漆黑陨铁
+ 114033 or 114034 or 114035 or 114036 => true, // 今昔剧画
+ 114045 or 114046 or 114047 or 114048 => true, // 谧林涓露
+ _ => false,
+ },
+ _ => false,
+ };
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/CookBonusView.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/CookBonusView.cs
new file mode 100644
index 0000000000..aa9fc7d3ef
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/Hutao/CookBonusView.cs
@@ -0,0 +1,46 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Metadata;
+using Snap.Hutao.Model.Metadata.Avatar;
+using Snap.Hutao.Model.Primitive;
+
+namespace Snap.Hutao.Model.Binding.Hutao;
+
+///
+/// 料理奖励视图
+///
+public class CookBonusView
+{
+ ///
+ /// 原型
+ ///
+ public Material OriginItem { get; set; } = default!;
+
+ ///
+ /// 名称
+ ///
+ public Material Item { get; set; } = default!;
+
+ ///
+ /// 创建一个新的料理奖励视图
+ ///
+ /// 料理奖励
+ /// 材料映射
+ /// 新的料理奖励视图
+ public static CookBonusView? Create(CookBonus? cookBonus, Dictionary idMaterialMap)
+ {
+ if (cookBonus == null)
+ {
+ return null;
+ }
+
+ CookBonusView view = new()
+ {
+ OriginItem = idMaterialMap[cookBonus.OriginItemId],
+ Item = idMaterialMap[cookBonus.ItemId],
+ };
+
+ return view;
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs
index 47fbe2be79..79c11ffeef 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIF.cs
@@ -31,60 +31,4 @@ internal class UIIF
///
[JsonPropertyName("list")]
public List List { get; set; } = default!;
-}
-
-///
-/// UIIF物品
-///
-[JsonDerivedType(typeof(UIIFReliquary))]
-[JsonDerivedType(typeof(UIIFWeapon))]
-internal class UIIFItem
-{
- ///
- /// 物品Id
- ///
- [JsonPropertyName("itemId")]
- public int ItemId { get; set; }
-
- ///
- /// 物品Id
- ///
- [JsonPropertyName("count")]
- public int Count { get; set; }
-}
-
-///
-/// UIIF圣遗物
-///
-internal class UIIFReliquary : UIIFItem
-{
- ///
- /// 物品Id
- ///
- [JsonPropertyName("level")]
- public int Level { get; set; }
-
- ///
- /// 副属性列表
- ///
- [JsonPropertyName("appendPropIdList")]
- public List AppendPropIdList { get; set; } = default!;
-}
-
-///
-/// UIIF武器
-///
-internal class UIIFWeapon : UIIFItem
-{
- ///
- /// 物品Id
- ///
- [JsonPropertyName("level")]
- public int Level { get; set; }
-
- ///
- /// 精炼等级 0-4
- ///
- [JsonPropertyName("promoteLevel")]
- public int PromoteLevel { get; set; }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFItem.cs b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFItem.cs
new file mode 100644
index 0000000000..7314656b4b
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/InterChange/Inventory/UIIFItem.cs
@@ -0,0 +1,45 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using System.Collections.Immutable;
+
+namespace Snap.Hutao.Model.InterChange.Inventory;
+
+///
+/// UIIF物品
+///
+internal class UIIFItem
+{
+ ///
+ /// 物品Id
+ ///
+ [JsonPropertyName("itemId")]
+ public int ItemId { get; set; }
+
+ ///
+ /// 物品Id
+ ///
+ [JsonPropertyName("count")]
+ public int Count { get; set; }
+
+ ///
+ /// 等级
+ /// Reliquary/Weapon
+ ///
+ [JsonPropertyName("level")]
+ public int? Level { get; set; }
+
+ ///
+ /// 副属性列表
+ /// Reliquary
+ ///
+ [JsonPropertyName("appendPropIdList")]
+ public List? AppendPropIdList { get; set; } = default!;
+
+ ///
+ /// 精炼等级 0-4
+ /// Weapon
+ ///
+ [JsonPropertyName("promoteLevel")]
+ public int? PromoteLevel { get; set; }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs
new file mode 100644
index 0000000000..9def3dcd98
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Intrinsic/MaterialType.cs
@@ -0,0 +1,59 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Model.Intrinsic;
+
+///
+/// 材料类型
+///
+[SuppressMessage("", "SA1602")]
+public enum MaterialType
+{
+ MATERIAL_NONE = 0,
+ MATERIAL_FOOD = 1,
+ MATERIAL_QUEST = 2,
+ MATERIAL_EXCHANGE = 4,
+ MATERIAL_CONSUME,
+ MATERIAL_EXP_FRUIT,
+ MATERIAL_AVATAR,
+ MATERIAL_ADSORBATE,
+ MATERIAL_CRICKET,
+ MATERIAL_ELEM_CRYSTAL,
+ MATERIAL_WEAPON_EXP_STONE,
+ MATERIAL_CHEST,
+ MATERIAL_RELIQUARY_MATERIAL,
+ MATERIAL_AVATAR_MATERIAL,
+ MATERIAL_NOTICE_ADD_HP,
+ MATERIAL_SEA_LAMP,
+ MATERIAL_SELECTABLE_CHEST,
+ MATERIAL_FLYCLOAK,
+ MATERIAL_NAMECARD,
+ MATERIAL_TALENT,
+ MATERIAL_WIDGET,
+ MATERIAL_CHEST_BATCH_USE,
+ MATERIAL_FAKE_ABSORBATE,
+ MATERIAL_CONSUME_BATCH_USE,
+ MATERIAL_WOOD,
+ MATERIAL_FURNITURE_FORMULA = 27,
+ MATERIAL_CHANNELLER_SLAB_BUFF,
+ MATERIAL_FURNITURE_SUITE_FORMULA,
+ MATERIAL_COSTUME,
+ MATERIAL_HOME_SEED,
+ MATERIAL_FISH_BAIT,
+ MATERIAL_FISH_ROD,
+ MATERIAL_SUMO_BUFF, // never appear
+ MATERIAL_FIREWORKS,
+ MATERIAL_BGM,
+ MATERIAL_SPICE_FOOD,
+ MATERIAL_ACTIVITY_ROBOT,
+ MATERIAL_ACTIVITY_GEAR,
+ MATERIAL_ACTIVITY_JIGSAW,
+ MATERIAL_ARANARA,
+ MATERIAL_GCG_CARD,
+ MATERIAL_GCG_CARD_FACE, // 影幻卡面
+ MATERIAL_GCG_CARD_BACK,
+ MATERIAL_GCG_FIELD,
+ MATERIAL_DESHRET_MANUAL,
+ MATERIAL_RENAME_ITEM,
+ MATERIAL_GCG_EXCHANGE_ITEM,
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
index d7997f5c43..81a63f1aea 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.Implementation.cs
@@ -22,6 +22,17 @@ public partial class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQu
[JsonIgnore]
public ComplexAvatarCollocation? Collocation { get; set; }
+ ///
+ /// [非元数据] 烹饪奖励
+ ///
+ [JsonIgnore]
+ public CookBonusView? CookBonusView { get; set; }
+
+ ///
+ /// 养成物品视图
+ ///
+ public List? CultivationItemsView { get; set; }
+
///
public ICalculableAvatar ToCalculable()
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
index 65b5d65358..8eff5b97bf 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/Avatar.cs
@@ -81,5 +81,10 @@ public partial class Avatar
///
/// 皮肤
///
- public IEnumerable Costumes { get; set; } = default!;
+ public List Costumes { get; set; } = default!;
+
+ ///
+ /// 养成物品
+ ///
+ public List CultivationItems { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CookBonus.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CookBonus.cs
index 9fbdb57b30..888c4bd4c5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CookBonus.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/CookBonus.cs
@@ -1,7 +1,7 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
-using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata.Avatar;
@@ -13,45 +13,15 @@ public class CookBonus
///
/// 原型名称
///
- public string OriginName { get; set; } = default!;
-
- ///
- /// 原型描述
- ///
- public string OriginDescription { get; set; } = default!;
-
- ///
- /// 原型图标
- ///
- public string OriginIcon { get; set; } = default!;
+ public MaterialId OriginItemId { get; set; } = default!;
///
/// 名称
///
- public string Name { get; set; } = default!;
-
- ///
- /// 描述
- ///
- public string Description { get; set; } = default!;
-
- ///
- /// 效果描述
- ///
- public string EffectDescription { get; set; } = default!;
-
- ///
- /// 图标
- ///
- public string Icon { get; set; } = default!;
-
- ///
- /// 物品等级
- ///
- public ItemQuality RankLevel { get; set; }
+ public MaterialId ItemId { get; set; } = default!;
///
/// 材料列表
///
- public List InputList { get; set; } = default!;
+ public List InputList { get; set; } = default!;
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs
index b6c823a559..ba4cd8f684 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Avatar/FetterInfo.cs
@@ -83,7 +83,7 @@ public string BirthFormatted
///
/// 料理
///
- public CookBonus? CookBonus { get; set; }
+ public CookBonus? CookBonus2 { get; set; }
///
/// 好感语音
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs
index ca7b087c3b..9af3879c9a 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Converter/PropertyInfoDescriptor.cs
@@ -105,4 +105,4 @@ private static IList GetParameterInfos(IList parameters,
return results;
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs
index e46e897f2c..e580002b3c 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Metadata/Material.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Primitive;
namespace Snap.Hutao.Model.Metadata;
@@ -13,7 +14,7 @@ public class Material
///
/// 物品Id
///
- public int Id { get; set; }
+ public MaterialId Id { get; set; }
///
/// 等级
@@ -25,6 +26,11 @@ public class Material
///
public ItemType ItemType { get; set; }
+ ///
+ /// 材料类型
+ ///
+ public MaterialType MaterialType { get; set; }
+
///
/// 图标
///
@@ -44,4 +50,9 @@ public class Material
/// 类型描述
///
public string TypeDescription { get; set; } = default!;
+
+ ///
+ /// 效果描述
+ ///
+ public string? EffectDescription { get; set; }
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/MaterialId.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/MaterialId.cs
new file mode 100644
index 0000000000..59cbcb4060
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/MaterialId.cs
@@ -0,0 +1,71 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+using Snap.Hutao.Model.Primitive.Converter;
+
+namespace Snap.Hutao.Model.Primitive;
+
+///
+/// 3-6位 材料Id
+///
+[JsonConverter(typeof(IdentityConverter))]
+public readonly struct MaterialId : IEquatable, IComparable
+{
+ ///
+ /// 值
+ ///
+ public readonly int Value;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// value
+ public MaterialId(int value)
+ {
+ Value = value;
+ }
+
+ public static implicit operator int(MaterialId value)
+ {
+ return value.Value;
+ }
+
+ public static implicit operator MaterialId(int value)
+ {
+ return new(value);
+ }
+
+ public static bool operator ==(MaterialId left, MaterialId right)
+ {
+ return left.Value == right.Value;
+ }
+
+ public static bool operator !=(MaterialId left, MaterialId right)
+ {
+ return !(left == right);
+ }
+
+ ///
+ public int CompareTo(MaterialId other)
+ {
+ return Value.CompareTo(other.Value);
+ }
+
+ ///
+ public bool Equals(MaterialId other)
+ {
+ return Value == other.Value;
+ }
+
+ ///
+ public override bool Equals(object? obj)
+ {
+ return obj is MaterialId other && Equals(other);
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return Value.GetHashCode();
+ }
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs
index d274ae4423..70d2d4079b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/WeaponId.cs
@@ -62,4 +62,4 @@ public override int GetHashCode()
{
return Value.GetHashCode();
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
index 1a601fd0e8..0c64a13b20 100644
--- a/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
+++ b/src/Snap.Hutao/Snap.Hutao/NativeMethods.txt
@@ -4,8 +4,6 @@ WM_GETMINMAXINFO
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
-STDAPI
-
// Type definition
CWMO_FLAGS
HRESULT
diff --git a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
index 2e8b4b8138..0f77283457 100644
--- a/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
+++ b/src/Snap.Hutao/Snap.Hutao/Package.appxmanifest
@@ -12,7 +12,7 @@
+ Version="1.2.15.0" />
胡桃
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
index 4f11e774e6..95bb831f15 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Cultivation/CultivationService.cs
@@ -155,7 +155,8 @@ public async Task> GetCultivateEntri
List resultItems = new();
List items = await appDbContext.CultivateItems
.Where(i => i.EntryId == entryId)
- .OrderBy(i => i.ItemId).ToListAsync()
+ .OrderBy(i => i.ItemId)
+ .ToListAsync()
.ConfigureAwait(false);
foreach (CultivateItem item in items)
@@ -173,7 +174,7 @@ public async Task> GetCultivateEntri
results.Add(new(entry, itemBase, resultItems));
}
- return new(results);
+ return new(results.OrderByDescending(e => e.Items.Any(i => i.IsToday)));
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
index 130b29b100..e4b9942022 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/DailyNote/DailyNoteNotifier.cs
@@ -6,6 +6,7 @@
using Snap.Hutao.Context.Database;
using Snap.Hutao.Core.Database;
using Snap.Hutao.Model.Entity;
+using Snap.Hutao.Web.Hoyolab.Takumi.Auth;
using Snap.Hutao.Web.Hoyolab.Takumi.Binding;
using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.DailyNote;
@@ -23,7 +24,6 @@ internal class DailyNoteNotifier
/// 构造一个新的实时便笺通知器
///
/// 范围工厂
- /// 绑定客户端
/// 实时便笺入口
public DailyNoteNotifier(IServiceScopeFactory scopeFactory, DailyNoteEntry entry)
{
@@ -121,8 +121,17 @@ public async ValueTask NotifyAsync()
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService();
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService();
+ AuthClient authClient = scope.ServiceProvider.GetRequiredService();
+
+ string? actionTicket = await authClient
+ .GetActionTicketByStokenAsync("game_role", entry.User)
+ .ConfigureAwait(false);
+
+ List roles = await scope.ServiceProvider
+ .GetRequiredService()
+ .GetUserGameRolesByActionTicketAsync(actionTicket!, entry.User)
+ .ConfigureAwait(false);
- List roles = await bindingClient.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
ToastContentBuilder builder = new ToastContentBuilder()
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs
index a3e2c2aa6e..3c21465fc5 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/IMetadataService.cs
@@ -64,6 +64,13 @@ internal interface IMetadataService
/// Id到角色的字典
ValueTask> GetIdToAvatarMapAsync(CancellationToken token = default);
+ ///
+ /// 异步获取Id到材料的字典
+ ///
+ /// 取消令牌
+ /// Id到材料的字典
+ ValueTask> GetIdToMaterialMapAsync(CancellationToken token = default(CancellationToken));
+
///
/// 异步获取ID到圣遗物副词条的字典
///
diff --git a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs
index 8c27a5b744..40b3e530f1 100644
--- a/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Service/Metadata/MetadataService.Indexing.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT license.
using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Metadata.Reliquary;
using Snap.Hutao.Model.Metadata.Weapon;
@@ -26,6 +27,12 @@ public ValueTask> GetIdToAvatarMapAsync(Cancellatio
return FromCacheAsDictionaryAsync("Avatar", a => a.Id, token);
}
+ ///
+ public ValueTask> GetIdToMaterialMapAsync(CancellationToken token = default)
+ {
+ return FromCacheAsDictionaryAsync("Material", a => a.Id, token);
+ }
+
///
public ValueTask> GetIdReliquaryAffixMapAsync(CancellationToken token = default)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
index 6bc32b0df2..9b3d8d0b23 100644
--- a/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
+++ b/src/Snap.Hutao/Snap.Hutao/Snap.Hutao.csproj
@@ -71,6 +71,7 @@
+
@@ -183,6 +184,11 @@
+
+
+ MSBuild:Compile
+
+
MSBuild:Compile
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CommunityGameRecordDialog.xaml b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CommunityGameRecordDialog.xaml
new file mode 100644
index 0000000000..89b35a8dc1
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CommunityGameRecordDialog.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/CommunityGameRecordDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CommunityGameRecordDialog.xaml.cs
new file mode 100644
index 0000000000..8bf6f38604
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/CommunityGameRecordDialog.xaml.cs
@@ -0,0 +1,60 @@
+// Copyright (c) Microsoft Corporation and Contributors.
+// Licensed under the MIT License.
+
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using Microsoft.Web.WebView2.Core;
+using Snap.Hutao.Model.Binding.User;
+using Snap.Hutao.Service.User;
+using Snap.Hutao.Web.Bridge;
+
+namespace Snap.Hutao.View.Dialog;
+
+///
+/// Ϸ¼Ի
+///
+public sealed partial class CommunityGameRecordDialog : ContentDialog
+{
+ private readonly IServiceScope scope;
+ [SuppressMessage("", "IDE0052")]
+ private MiHoYoJSInterface? jsInterface;
+
+ ///
+ /// һµϷ¼Ի
+ ///
+ ///
+ public CommunityGameRecordDialog(MainWindow window)
+ {
+ InitializeComponent();
+ XamlRoot = window.Content.XamlRoot;
+ scope = Ioc.Default.CreateScope();
+ }
+
+ private void OnGridLoaded(object sender, RoutedEventArgs e)
+ {
+ InitializeAsync().SafeForget();
+ }
+
+ private async Task InitializeAsync()
+ {
+ await WebView.EnsureCoreWebView2Async();
+ CoreWebView2 coreWebView2 = WebView.CoreWebView2;
+ User? user = scope.ServiceProvider.GetRequiredService().Current;
+
+ if (user == null)
+ {
+ return;
+ }
+
+ coreWebView2.SetCookie(user.CookieToken, user.Ltoken, null).SetMobileUserAgent();
+ jsInterface = new(coreWebView2, scope.ServiceProvider);
+ coreWebView2.Navigate("https://webstatic.mihoyo.com/app/community-game-records/index.html");
+ }
+
+ private void OnContentDialogClosed(ContentDialog sender, ContentDialogClosedEventArgs args)
+ {
+ jsInterface = null;
+ scope.Dispose();
+ }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs b/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs
index b0f5e8e215..8da9a813a7 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs
+++ b/src/Snap.Hutao/Snap.Hutao/View/Dialog/SignInWebViewDialog.xaml.cs
@@ -39,8 +39,7 @@ private async Task InitializeAsync()
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
- IUserService userService = scope.ServiceProvider.GetRequiredService();
- User? user = userService.Current;
+ User? user = scope.ServiceProvider.GetRequiredService().Current;
if (user == null)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
index 483884aa9d..3ac6b3eaa6 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/AnnouncementPage.xaml
@@ -142,6 +142,13 @@
+
+
+
+
+
+
+
1
+
+
@@ -71,6 +77,7 @@
-
+
@@ -115,64 +122,65 @@
ToolTipService.ToolTip="删除清单"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
index d7d2b50b02..2022a62947 100644
--- a/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
+++ b/src/Snap.Hutao/Snap.Hutao/View/Page/WikiAvatarPage.xaml
@@ -178,13 +178,13 @@
-
+
@@ -286,6 +291,24 @@
Content="{Binding Selected.Property, Mode=OneWay}"
ContentTemplate="{StaticResource PropertyDataTemplate}"/>
+
+
+
+
+
+
+
+
+
+
+
@@ -405,12 +426,7 @@
Margin="16,32,0,0"
Style="{StaticResource BaseTextBlockStyle}"
Text="其他"/>
-
+
@@ -443,7 +459,7 @@
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Header="料理">
-
+
@@ -451,7 +467,6 @@
-
@@ -465,8 +480,8 @@
Grid.Row="1"
Grid.Column="0"
Margin="16,16,0,16"
- Text="{Binding Selected.FetterInfo.CookBonus.Name}">
-
+ Text="{Binding Item.Name}">
+
-
+ Text="{Binding OriginItem.Name}">
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Margin="16">
+
+
+
+
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
index aeb320efb8..b8068b5c28 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/SettingViewModel.cs
@@ -185,6 +185,37 @@ public NamedValue SelectedBackdropType
///
public ICommand ShowSignInWebViewDialogCommand { get; }
+ private static async Task DangerousUnusedLoginMethodAsync()
+ {
+ LoginMihoyoBBSDialog dialog = ActivatorUtilities.CreateInstance(Ioc.Default);
+ (bool isOk, Dictionary? data) = await dialog.GetInputAccountPasswordAsync().ConfigureAwait(false);
+
+ if (isOk)
+ {
+ (Response? resp, Aigis? aigis) = await Ioc.Default
+ .GetRequiredService()
+ .LoginByPasswordAsync(data, CancellationToken.None)
+ .ConfigureAwait(false);
+
+ if (resp != null)
+ {
+ if (resp.IsOk())
+ {
+ Cookie cookie = Cookie.FromLoginResult(resp.Data);
+
+ await Ioc.Default
+ .GetRequiredService()
+ .ProcessInputCookieAsync(cookie)
+ .ConfigureAwait(false);
+ }
+
+ if (resp.ReturnCode == (int)KnownReturnCode.RET_NEED_AIGIS)
+ {
+ }
+ }
+ }
+ }
+
private async Task SetGamePathAsync()
{
IGameLocator locator = Ioc.Default.GetRequiredService>()
@@ -221,33 +252,10 @@ private async Task ShowSignInWebViewDialogAsync()
private async Task DebugThrowExceptionAsync()
{
#if DEBUG
- LoginMihoyoBBSDialog dialog = ActivatorUtilities.CreateInstance(Ioc.Default);
- (bool isOk, Dictionary? data) = await dialog.GetInputAccountPasswordAsync().ConfigureAwait(false);
-
- if (isOk)
- {
- (Response? resp, Aigis? aigis) = await Ioc.Default
- .GetRequiredService()
- .LoginByPasswordAsync(data, CancellationToken.None)
- .ConfigureAwait(false);
-
- if (resp != null)
- {
- if (resp.IsOk())
- {
- Cookie cookie = Cookie.FromLoginResult(resp.Data);
-
- await Ioc.Default
- .GetRequiredService()
- .ProcessInputCookieAsync(cookie)
- .ConfigureAwait(false);
- }
-
- if (resp.ReturnCode == (int)KnownReturnCode.RET_NEED_AIGIS)
- {
- }
- }
- }
+ CommunityGameRecordDialog dialog = ActivatorUtilities.CreateInstance(Ioc.Default);
+ await dialog.ShowAsync();
+#else
+ await Task.Yield();
#endif
}
}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
index b1c310875e..6b92301813 100644
--- a/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
+++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/WikiAvatarViewModel.cs
@@ -10,6 +10,7 @@
using Snap.Hutao.Model.Binding.Cultivation;
using Snap.Hutao.Model.Binding.Hutao;
using Snap.Hutao.Model.Intrinsic;
+using Snap.Hutao.Model.Metadata;
using Snap.Hutao.Model.Metadata.Avatar;
using Snap.Hutao.Model.Primitive;
using Snap.Hutao.Service.Abstraction;
@@ -89,13 +90,14 @@ private async Task OpenUIAsync()
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
+ Dictionary idMaterialMap = await metadataService.GetIdToMaterialMapAsync().ConfigureAwait(false);
List avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false);
List sorted = avatars
.OrderByDescending(avatar => avatar.BeginTime)
.ThenByDescending(avatar => avatar.Sort)
.ToList();
- await CombineWithAvatarCollocationsAsync(sorted).ConfigureAwait(false);
+ await CombineComplexDataAsync(sorted, idMaterialMap).ConfigureAwait(false);
await ThreadHelper.SwitchToMainThreadAsync();
Avatars = new AdvancedCollectionView(sorted, true);
@@ -103,7 +105,7 @@ private async Task OpenUIAsync()
}
}
- private async Task CombineWithAvatarCollocationsAsync(List avatars)
+ private async Task CombineComplexDataAsync(List avatars, Dictionary idMaterialMap)
{
if (await hutaoCache.InitializeForWikiAvatarViewModelAsync().ConfigureAwait(false))
{
@@ -112,6 +114,8 @@ private async Task CombineWithAvatarCollocationsAsync(List avatars)
foreach (Avatar avatar in avatars)
{
avatar.Collocation = idCollocations.GetValueOrDefault(avatar.Id);
+ avatar.CookBonusView ??= CookBonusView.Create(avatar.FetterInfo.CookBonus2, idMaterialMap);
+ avatar.CultivationItemsView ??= avatar.CultivationItems.Select(i => idMaterialMap[i]).ToList();
}
}
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs b/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs
index 813a846286..feaa34ec7b 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/ApiEndpoints.cs
@@ -311,6 +311,14 @@ public static string GachaInfoGetGachaLog(string query)
public const string AccountCreateActionTicket = $"{PassportApi}/account/ma-cn-verifier/app/createActionTicketByToken";
#endregion
+ #region Patcher
+
+ ///
+ /// 胡桃检查更新
+ ///
+ public const string PatcherHutaoStable = $"{PatcherApi}/hutao/stable";
+ #endregion
+
#region SdkStaticLauncherApi
///
@@ -360,6 +368,8 @@ public static string SdkStaticLauncherResource(string launcherId, string channel
private const string PassportApiAuthApi = $"{PassportApi}/account/auth/api";
private const string PassportApiV4 = "https://passport-api-v4.mihoyo.com";
+ private const string PatcherApi = "https://patcher.dgp-studio.cn";
+
private const string SdkStatic = "https://sdk-static.mihoyo.com";
private const string SdkStaticLauncherApi = $"{SdkStatic}/hk4e_cn/mdk/launcher/api";
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
index 02990e3098..c44cabc0a3 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/MiHoYoJSInterface.cs
@@ -48,7 +48,7 @@ public class MiHoYoJSInterface
///
/// webview2
/// 服务提供器
- protected MiHoYoJSInterface(CoreWebView2 webView, IServiceProvider serviceProvider)
+ public MiHoYoJSInterface(CoreWebView2 webView, IServiceProvider serviceProvider)
{
this.webView = webView;
this.serviceProvider = serviceProvider;
@@ -159,7 +159,7 @@ public virtual JsResult> GetDynamicSecrectV2(JsParam<
long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
int r = GetRandom();
string b = param.Payload.Body;
- string q = string.Join('&', param.Payload.Query.OrderBy(x => x.Key).Select(x => $"{x.Key}={x.Value}"));
+ string q = param.Payload.GetQueryParam();
string check = Md5Convert.ToHexString($"salt={salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant();
return new() { Data = new() { ["DS"] = $"{t},{r},{check}", }, };
@@ -256,6 +256,13 @@ public virtual JsResult> GetStatusBarHeight(JsParam p
return new() { Data = new() { { "statusBarHeight", 0 } } };
}
+ [JsMethod("pushPage")]
+ public virtual IJsResult? PushPage(JsParam param)
+ {
+ webView.Navigate(param.Payload.Page);
+ return null;
+ }
+
[JsMethod("showAlertDialog")]
public virtual Task ShowAlertDialogAsync(JsParam param)
{
@@ -286,12 +293,6 @@ public virtual JsResult> GetStatusBarHeight(JsParam p
throw new NotImplementedException();
}
- [JsMethod("pushPage")]
- public virtual IJsResult? PushPage(JsParam param)
- {
- throw new NotImplementedException();
- }
-
[JsMethod("openSystemBrowser")]
public virtual IJsResult? OpenSystemBrowser(JsParam param)
{
@@ -344,7 +345,7 @@ private async Task ExecuteCallbackScriptAsync(string callback, string? p
private async void OnWebMessageReceived(CoreWebView2 webView2, CoreWebView2WebMessageReceivedEventArgs args)
{
string message = args.TryGetWebMessageAsString();
-
+ logger.LogInformation("[OnRawMessage]\n{message}", message);
JsParam param = JsonSerializer.Deserialize(message)!;
logger.LogInformation("[OnMessage]\nMethod : {method}\nPayload : {payload}\nCallback: {callback}", param.Method, param.Payload, param.Callback);
@@ -363,6 +364,7 @@ private async void OnWebMessageReceived(CoreWebView2 webView2, CoreWebView2WebMe
"getUserInfo" => GetUserInfo(param),
"hideLoading" => null,
"login" => null,
+ "pushPage" => PushPage(param),
"showLoading" => null,
_ => logger.LogWarning("Unhandled Message Type: {method}", param.Method),
};
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs
index 8505c3b21c..97447e153e 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/DynamicSecrect2Playload.cs
@@ -12,11 +12,30 @@ public class DynamicSecrect2Playload
/// q
///
[JsonPropertyName("query")]
- public Dictionary Query { get; set; } = default!;
+ public Dictionary Query { get; set; } = default!;
///
/// b
///
[JsonPropertyName("body")]
public string Body { get; set; } = default!;
+
+ ///
+ /// 获取排序后的的查询参数
+ ///
+ /// 查询参数
+ public string GetQueryParam()
+ {
+ IEnumerable parts = Query.OrderBy(x => x.Key).Select(x =>
+ {
+ if (x.Value.ValueKind == JsonValueKind.True || x.Value.ValueKind == JsonValueKind.False)
+ {
+ return $"{x.Key}={x.Value.ToString().ToLowerInvariant()}";
+ }
+
+ return $"{x.Key}={x.Value}";
+ });
+
+ return string.Join('&', parts);
+ }
}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
index 2eb39f4bf8..61e59145cd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/JsParam.cs
@@ -63,4 +63,4 @@ public static implicit operator JsParam(JsParam jsParam)
Callback = jsParam.Callback,
};
}
-}
\ No newline at end of file
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/PushPagePayload.cs b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/PushPagePayload.cs
new file mode 100644
index 0000000000..b0ed80d87c
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Bridge/Model/PushPagePayload.cs
@@ -0,0 +1,16 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Bridge.Model;
+
+///
+/// 导航页面参数
+///
+public class PushPagePayload
+{
+ ///
+ /// 页面Url
+ ///
+ [JsonPropertyName("page")]
+ public string Page { get; set; } = default!;
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs
index 789eb0abf6..9313031cdd 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient.cs
@@ -59,6 +59,7 @@ public async Task> GetUserGameRolesByActionTicketAsync(string
/// 用户
/// 取消令牌
/// 用户角色信息
+ [Obsolete("Set-Cookie 冲突")]
[ApiInformation(Cookie = CookieType.Ltoken)]
public async Task> GetUserGameRolesByCookieAsync(User user, CancellationToken token = default)
{
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs
index da1cf2a1a9..8f08b9e7ed 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Binding/BindingClient2.cs
@@ -41,12 +41,12 @@ public BindingClient2(HttpClient httpClient, JsonSerializerOptions options, ILog
/// 用户
/// 取消令牌
/// 用户角色信息
- [ApiInformation(Cookie = CookieType.Stoken)]
+ [ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.LK2)]
public async Task> GetUserGameRolesByStokenAsync(User user, CancellationToken token = default)
{
Response>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
- .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true)
+ .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.LK2, true)
.TryCatchGetFromJsonAsync>>(ApiEndpoints.UserGameRolesByStoken, options, logger, token)
.ConfigureAwait(false);
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
index 522ac6e338..f50bd00b9f 100644
--- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs
@@ -19,7 +19,6 @@ namespace Snap.Hutao.Web.Hutao;
///
/// 胡桃API客户端
///
-// [Injection(InjectAs.Transient)]
[HttpClient(HttpClientConfigration.Default)]
internal class HomaClient
{
@@ -220,4 +219,4 @@ public async Task GetPlayerRecordAsync(User user, CancellationToke
{
return httpClient.TryCatchPostAsJsonAsync>($"{HutaoAPI}/Record/Upload", playerRecord, options, logger, token);
}
-}
+}
\ No newline at end of file
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Patch/PatchInformation.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Patch/PatchInformation.cs
new file mode 100644
index 0000000000..405ad030d3
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/Patch/PatchInformation.cs
@@ -0,0 +1,30 @@
+// Copyright (c) DGP Studio. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Snap.Hutao.Web.Hutao.Patch;
+
+///
+/// 更新信息
+///
+public class PatchInformation
+{
+ ///
+ /// 标签名 通常被替换为版本
+ ///
+ public string TagName { get; set; } = default!;
+
+ ///
+ /// 更新日志
+ ///
+ public string Body { get; set; } = default!;
+
+ ///
+ /// 浏览器下载链接
+ ///
+ public Uri BrowserDownloadUrl { get; set; } = default!;
+
+ ///
+ /// 缓存时间
+ ///
+ public DateTimeOffset CacheTime { get; set; }
+}
diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/PatchClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/PatchClient.cs
new file mode 100644
index 0000000000..40f0362258
--- /dev/null
+++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/PatchClient.cs
@@ -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.Extension;
+using Snap.Hutao.Model.Binding.User;
+using Snap.Hutao.Web.Hoyolab;
+using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord;
+using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar;
+using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.SpiralAbyss;
+using Snap.Hutao.Web.Hutao.Model;
+using Snap.Hutao.Web.Hutao.Model.Post;
+using Snap.Hutao.Web.Response;
+using System.Net.Http;
+using System.Net.Http.Json;
+
+namespace Snap.Hutao.Web.Hutao;
+
+///
+/// 更新客户端
+///
+[HttpClient(HttpClientConfigration.Default)]
+internal class PatchClient
+{
+ private readonly HttpClient httpClient;
+ private readonly JsonSerializerOptions options;
+ private readonly ILogger logger;
+
+ ///
+ /// 构造一个新的更新客户端
+ ///
+ /// http 客户端
+ /// json选项
+ /// 日志器
+ public PatchClient(HttpClient httpClient, JsonSerializerOptions options, ILogger logger)
+ {
+ this.httpClient = httpClient;
+ this.options = options;
+ this.logger = logger;
+ }
+
+ ///
+ /// 更新信息
+ ///
+ ///
+ ///
+ public Task GetPatchInformationAsync(CancellationToken token = default)
+ {
+ return httpClient.TryCatchGetFromJsonAsync(ApiEndpoints.PatcherHutaoStable, options, logger, token);
+ }
+}
\ No newline at end of file