cultivation optimization

This commit is contained in:
DismissedLight
2022-12-22 14:20:19 +08:00
parent bbc8324f5d
commit a97bab8a1c
41 changed files with 763 additions and 279 deletions

View File

@@ -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)

View File

@@ -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 @@ internal class PickerFactory : IPickerFactory
picker.FileTypeFilter.Add(type);
}
// https://github.com/microsoft/WindowsAppSDK/issues/2931
picker.FileTypeFilter.Add(AnyType);
return picker;

View File

@@ -25,6 +25,7 @@ public class CultivateItem : ObservableObject
Inner = inner;
Entity = entity;
isFinished = Entity.IsFinished;
IsToday = CultivateItemHelper.IsTodaysMaterial(inner.Id, DateTimeOffset.Now);
FinishStateCommand = new RelayCommand(FlipIsFinished);
}
@@ -59,6 +60,11 @@ public class CultivateItem : ObservableObject
}
}
/// <summary>
/// 是否为今日物品
/// </summary>
public bool IsToday { get; }
private void FlipIsFinished()
{
IsFinished = !IsFinished;

View File

@@ -0,0 +1,64 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Binding.Cultivation;
/// <summary>
/// 养成物品帮助类
/// </summary>
public static class CultivateItemHelper
{
/// <summary>
/// 判断是否为当日物品
/// </summary>
/// <param name="itemId">材料Id</param>
/// <param name="now">时间</param>
/// <returns>是否为当日物品</returns>
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,
};
}
}

View File

@@ -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;
/// <summary>
/// 料理奖励视图
/// </summary>
public class CookBonusView
{
/// <summary>
/// 原型
/// </summary>
public Material OriginItem { get; set; } = default!;
/// <summary>
/// 名称
/// </summary>
public Material Item { get; set; } = default!;
/// <summary>
/// 创建一个新的料理奖励视图
/// </summary>
/// <param name="cookBonus">料理奖励</param>
/// <param name="idMaterialMap">材料映射</param>
/// <returns>新的料理奖励视图</returns>
public static CookBonusView? Create(CookBonus? cookBonus, Dictionary<MaterialId, Material> idMaterialMap)
{
if (cookBonus == null)
{
return null;
}
CookBonusView view = new()
{
OriginItem = idMaterialMap[cookBonus.OriginItemId],
Item = idMaterialMap[cookBonus.ItemId],
};
return view;
}
}

View File

@@ -31,60 +31,4 @@ internal class UIIF
/// </summary>
[JsonPropertyName("list")]
public List<UIIFItem> List { get; set; } = default!;
}
/// <summary>
/// UIIF物品
/// </summary>
[JsonDerivedType(typeof(UIIFReliquary))]
[JsonDerivedType(typeof(UIIFWeapon))]
internal class UIIFItem
{
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("itemId")]
public int ItemId { get; set; }
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("count")]
public int Count { get; set; }
}
/// <summary>
/// UIIF圣遗物
/// </summary>
internal class UIIFReliquary : UIIFItem
{
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("level")]
public int Level { get; set; }
/// <summary>
/// 副属性列表
/// </summary>
[JsonPropertyName("appendPropIdList")]
public List<int> AppendPropIdList { get; set; } = default!;
}
/// <summary>
/// UIIF武器
/// </summary>
internal class UIIFWeapon : UIIFItem
{
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("level")]
public int Level { get; set; }
/// <summary>
/// 精炼等级 0-4
/// </summary>
[JsonPropertyName("promoteLevel")]
public int PromoteLevel { get; set; }
}

View File

@@ -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;
/// <summary>
/// UIIF物品
/// </summary>
internal class UIIFItem
{
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("itemId")]
public int ItemId { get; set; }
/// <summary>
/// 物品Id
/// </summary>
[JsonPropertyName("count")]
public int Count { get; set; }
/// <summary>
/// 等级
/// Reliquary/Weapon
/// </summary>
[JsonPropertyName("level")]
public int? Level { get; set; }
/// <summary>
/// 副属性列表
/// Reliquary
/// </summary>
[JsonPropertyName("appendPropIdList")]
public List<int>? AppendPropIdList { get; set; } = default!;
/// <summary>
/// 精炼等级 0-4
/// Weapon
/// </summary>
[JsonPropertyName("promoteLevel")]
public int? PromoteLevel { get; set; }
}

View File

@@ -0,0 +1,59 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Model.Intrinsic;
/// <summary>
/// 材料类型
/// </summary>
[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,
}

View File

@@ -22,6 +22,17 @@ public partial class Avatar : IStatisticsItemSource, ISummaryItemSource, INameQu
[JsonIgnore]
public ComplexAvatarCollocation? Collocation { get; set; }
/// <summary>
/// [非元数据] 烹饪奖励
/// </summary>
[JsonIgnore]
public CookBonusView? CookBonusView { get; set; }
/// <summary>
/// 养成物品视图
/// </summary>
public List<Material>? CultivationItemsView { get; set; }
/// <inheritdoc/>
public ICalculableAvatar ToCalculable()
{

View File

@@ -81,5 +81,10 @@ public partial class Avatar
/// <summary>
/// 皮肤
/// </summary>
public IEnumerable<Costume> Costumes { get; set; } = default!;
public List<Costume> Costumes { get; set; } = default!;
/// <summary>
/// 养成物品
/// </summary>
public List<MaterialId> CultivationItems { get; set; } = default!;
}

View File

@@ -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
/// <summary>
/// 原型名称
/// </summary>
public string OriginName { get; set; } = default!;
/// <summary>
/// 原型描述
/// </summary>
public string OriginDescription { get; set; } = default!;
/// <summary>
/// 原型图标
/// </summary>
public string OriginIcon { get; set; } = default!;
public MaterialId OriginItemId { get; set; } = default!;
/// <summary>
/// 名称
/// </summary>
public string Name { get; set; } = default!;
/// <summary>
/// 描述
/// </summary>
public string Description { get; set; } = default!;
/// <summary>
/// 效果描述
/// </summary>
public string EffectDescription { get; set; } = default!;
/// <summary>
/// 图标
/// </summary>
public string Icon { get; set; } = default!;
/// <summary>
/// 物品等级
/// </summary>
public ItemQuality RankLevel { get; set; }
public MaterialId ItemId { get; set; } = default!;
/// <summary>
/// 材料列表
/// </summary>
public List<ItemWithCount> InputList { get; set; } = default!;
public List<MaterialId> InputList { get; set; } = default!;
}

View File

@@ -83,7 +83,7 @@ public class FetterInfo
/// <summary>
/// 料理
/// </summary>
public CookBonus? CookBonus { get; set; }
public CookBonus? CookBonus2 { get; set; }
/// <summary>
/// 好感语音

View File

@@ -105,4 +105,4 @@ internal class PropertyInfoDescriptor : ValueConverterBase<PropertyInfo, IList<L
return results;
}
}
}

View File

@@ -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
/// <summary>
/// 物品Id
/// </summary>
public int Id { get; set; }
public MaterialId Id { get; set; }
/// <summary>
/// 等级
@@ -25,6 +26,11 @@ public class Material
/// </summary>
public ItemType ItemType { get; set; }
/// <summary>
/// 材料类型
/// </summary>
public MaterialType MaterialType { get; set; }
/// <summary>
/// 图标
/// </summary>
@@ -44,4 +50,9 @@ public class Material
/// 类型描述
/// </summary>
public string TypeDescription { get; set; } = default!;
/// <summary>
/// 效果描述
/// </summary>
public string? EffectDescription { get; set; }
}

View File

@@ -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;
/// <summary>
/// 3-6位 材料Id
/// </summary>
[JsonConverter(typeof(IdentityConverter<MaterialId>))]
public readonly struct MaterialId : IEquatable<MaterialId>, IComparable<MaterialId>
{
/// <summary>
/// 值
/// </summary>
public readonly int Value;
/// <summary>
/// Initializes a new instance of the <see cref="MaterialId"/> struct.
/// </summary>
/// <param name="value">value</param>
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);
}
/// <inheritdoc/>
public int CompareTo(MaterialId other)
{
return Value.CompareTo(other.Value);
}
/// <inheritdoc/>
public bool Equals(MaterialId other)
{
return Value == other.Value;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return obj is MaterialId other && Equals(other);
}
/// <inheritdoc/>
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -62,4 +62,4 @@ public readonly struct WeaponId : IEquatable<WeaponId>
{
return Value.GetHashCode();
}
}
}

View File

@@ -4,8 +4,6 @@ WM_GETMINMAXINFO
WM_NCRBUTTONDOWN
WM_NCRBUTTONUP
STDAPI
// Type definition
CWMO_FLAGS
HRESULT

View File

@@ -12,7 +12,7 @@
<Identity
Name="7f0db578-026f-4e0b-a75b-d5d06bb0a74d"
Publisher="CN=DGP Studio"
Version="1.2.14.0" />
Version="1.2.15.0" />
<Properties>
<DisplayName>胡桃</DisplayName>

View File

@@ -155,7 +155,8 @@ internal class CultivationService : ICultivationService
List<BindingCultivateItem> resultItems = new();
List<CultivateItem> 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 @@ internal class CultivationService : ICultivationService
results.Add(new(entry, itemBase, resultItems));
}
return new(results);
return new(results.OrderByDescending(e => e.Items.Any(i => i.IsToday)));
}
}

View File

@@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
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
/// 构造一个新的实时便笺通知器
/// </summary>
/// <param name="scopeFactory">范围工厂</param>
/// <param name="bindingClient">绑定客户端</param>
/// <param name="entry">实时便笺入口</param>
public DailyNoteNotifier(IServiceScopeFactory scopeFactory, DailyNoteEntry entry)
{
@@ -121,8 +121,17 @@ internal class DailyNoteNotifier
{
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService<BindingClient>();
AuthClient authClient = scope.ServiceProvider.GetRequiredService<AuthClient>();
string? actionTicket = await authClient
.GetActionTicketByStokenAsync("game_role", entry.User)
.ConfigureAwait(false);
List<UserGameRole> roles = await scope.ServiceProvider
.GetRequiredService<BindingClient>()
.GetUserGameRolesByActionTicketAsync(actionTicket!, entry.User)
.ConfigureAwait(false);
List<UserGameRole> roles = await bindingClient.GetUserGameRolesByCookieAsync(entry.User).ConfigureAwait(false);
string attribution = roles.SingleOrDefault(r => r.GameUid == entry.Uid)?.ToString() ?? "未知角色";
ToastContentBuilder builder = new ToastContentBuilder()

View File

@@ -64,6 +64,13 @@ internal interface IMetadataService
/// <returns>Id到角色的字典</returns>
ValueTask<Dictionary<AvatarId, Avatar>> GetIdToAvatarMapAsync(CancellationToken token = default);
/// <summary>
/// 异步获取Id到材料的字典
/// </summary>
/// <param name="token">取消令牌</param>
/// <returns>Id到材料的字典</returns>
ValueTask<Dictionary<MaterialId, Material>> GetIdToMaterialMapAsync(CancellationToken token = default(CancellationToken));
/// <summary>
/// 异步获取ID到圣遗物副词条的字典
/// </summary>

View File

@@ -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 @@ internal partial class MetadataService
return FromCacheAsDictionaryAsync<AvatarId, Avatar>("Avatar", a => a.Id, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<MaterialId, Material>> GetIdToMaterialMapAsync(CancellationToken token = default)
{
return FromCacheAsDictionaryAsync<MaterialId, Material>("Material", a => a.Id, token);
}
/// <inheritdoc/>
public ValueTask<Dictionary<ReliquaryAffixId, ReliquaryAffix>> GetIdReliquaryAffixMapAsync(CancellationToken token = default)
{

View File

@@ -71,6 +71,7 @@
<None Remove="View\Dialog\AchievementImportDialog.xaml" />
<None Remove="View\Dialog\AdoptCalculatorDialog.xaml" />
<None Remove="View\Dialog\AvatarInfoQueryDialog.xaml" />
<None Remove="View\Dialog\CommunityGameRecordDialog.xaml" />
<None Remove="View\Dialog\CultivateProjectDialog.xaml" />
<None Remove="View\Dialog\CultivatePromotionDeltaDialog.xaml" />
<None Remove="View\Dialog\DailyNoteNotificationDialog.xaml" />
@@ -183,6 +184,11 @@
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
<ItemGroup>
<Page Update="View\Dialog\CommunityGameRecordDialog.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="View\Dialog\CultivatePromotionDeltaDialog.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,22 @@
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
<!-- Licensed under the MIT License. -->
<ContentDialog
x:Class="Snap.Hutao.View.Dialog.CommunityGameRecordDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Closed="OnContentDialogClosed"
DefaultButton="Primary"
PrimaryButtonText="完成"
Style="{StaticResource DefaultContentDialogStyle}"
mc:Ignorable="d">
<Grid Loaded="OnGridLoaded">
<WebView2
Name="WebView"
Width="360"
Height="580"/>
</Grid>
</ContentDialog>

View File

@@ -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;
/// <summary>
/// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7>¼<EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
/// </summary>
public sealed partial class CommunityGameRecordDialog : ContentDialog
{
private readonly IServiceScope scope;
[SuppressMessage("", "IDE0052")]
private MiHoYoJSInterface? jsInterface;
/// <summary>
/// <20><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD>µ<EFBFBD><C2B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϸ<EFBFBD><CFB7>¼<EFBFBD>Ի<EFBFBD><D4BB><EFBFBD>
/// </summary>
/// <param name="window"><3E><><EFBFBD><EFBFBD></param>
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<IUserService>().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();
}
}

View File

@@ -39,8 +39,7 @@ public sealed partial class SignInWebViewDialog : ContentDialog
{
await WebView.EnsureCoreWebView2Async();
CoreWebView2 coreWebView2 = WebView.CoreWebView2;
IUserService userService = scope.ServiceProvider.GetRequiredService<IUserService>();
User? user = userService.Current;
User? user = scope.ServiceProvider.GetRequiredService<IUserService>().Current;
if (user == null)
{

View File

@@ -142,6 +142,13 @@
<Grid>
<ScrollViewer Padding="0,0,4,0">
<StackPanel>
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<InfoBar/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Pivot>
<PivotItem
Content="{Binding Announcement.List[0]}"

View File

@@ -2,6 +2,7 @@
x:Class="Snap.Hutao.View.Page.CultivationPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cwua="using:CommunityToolkit.WinUI.UI.Animations"
xmlns:cwucont="using:CommunityToolkit.WinUI.UI.Controls"
xmlns:cwuconv="using:CommunityToolkit.WinUI.UI.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
@@ -29,6 +30,11 @@
<x:Double>1</x:Double>
</cwuconv:BoolToObjectConverter.FalseValue>
</cwuconv:BoolToObjectConverter>
<cwuconv:BoolToObjectConverter
x:Key="BoolToStyleSelector"
FalseValue="{StaticResource BodyTextBlockStyle}"
TrueValue="{StaticResource BaseTextBlockStyle}"/>
</Page.Resources>
<mxi:Interaction.Behaviors>
@@ -71,6 +77,7 @@
<PivotItem Header="材料清单">
<cwucont:AdaptiveGridView
Padding="16,16,4,4"
cwua:ItemsReorderAnimation.Duration="0:0:0.1"
DesiredWidth="320"
ItemContainerStyle="{StaticResource LargeGridViewItemStyle}"
ItemsSource="{Binding CultivateEntries}"
@@ -81,7 +88,7 @@
<Grid Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid Margin="8">
<Grid.ColumnDefinitions>
@@ -115,64 +122,65 @@
ToolTipService.ToolTip="删除清单"/>
</StackPanel>
</Grid>
<ItemsControl
Grid.Row="1"
Margin="8,0,8,8"
ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,4,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid>
<shvc:ItemIcon
Width="32"
Height="32"
Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}"
Opacity="{Binding IsFinished, Converter={StaticResource BoolToOpacityConverter}}"
Quality="{Binding Inner.RankLevel}"/>
<FontIcon
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Glyph="&#xE73E;"
Visibility="{Binding IsFinished, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
<Button
Grid.Column="1"
Height="32"
Margin="6,0,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Background="Transparent"
BorderBrush="{x:Null}"
BorderThickness="0"
Command="{Binding FinishStateCommand}">
<Grid Opacity="{Binding IsFinished, Converter={StaticResource BoolToOpacityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="0,0,0,0"
<ScrollViewer Grid.Row="1" Height="240">
<ItemsControl Margin="8,0,8,8" ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,4,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid>
<shvc:ItemIcon
Width="32"
Height="32"
Icon="{Binding Inner.Icon, Converter={StaticResource ItemIconConverter}}"
Opacity="{Binding IsFinished, Converter={StaticResource BoolToOpacityConverter}}"
Quality="{Binding Inner.RankLevel}"/>
<FontIcon
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Inner.Name}"
TextTrimming="CharacterEllipsis"/>
<TextBlock
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{Binding Entity.Count}"/>
FontSize="24"
Glyph="&#xE73E;"
Visibility="{Binding IsFinished, Converter={StaticResource BoolToVisibilityConverter}}"/>
</Grid>
</Button>
<Button
Grid.Column="1"
Height="32"
Margin="6,0,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Background="Transparent"
BorderBrush="{x:Null}"
BorderThickness="0"
Command="{Binding FinishStateCommand}">
<Grid Opacity="{Binding IsFinished, Converter={StaticResource BoolToOpacityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
Margin="0,0,0,0"
VerticalAlignment="Center"
Style="{Binding IsToday, Converter={StaticResource BoolToStyleSelector}}"
Text="{Binding Inner.Name}"
TextTrimming="CharacterEllipsis"/>
<TextBlock
Grid.Column="1"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Style="{Binding IsToday, Converter={StaticResource BoolToStyleSelector}}"
Text="{Binding Entity.Count}"/>
</Grid>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<Grid.Resources>
<Storyboard x:Name="ButtonPanelVisibleStoryboard">

View File

@@ -178,13 +178,13 @@
</Grid.ColumnDefinitions>
<shci:MonoChrome
Grid.Column="0"
Width="27.2"
Height="27.2"
Width="32"
Height="32"
Source="{Binding Selected.FetterInfo.VisionBefore, Converter={StaticResource ElementNameIconConverter}}"/>
<shci:MonoChrome
Grid.Column="1"
Width="27.2"
Height="27.2"
Width="32"
Height="32"
Source="{Binding Selected.Weapon, Converter={StaticResource WeaponTypeIconConverter}}"/>
</Grid>
<shvc:ItemIcon
@@ -196,9 +196,14 @@
<StackPanel Grid.Column="1" Margin="16,0,0,0">
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="{Binding Selected.Name}"/>
<TextBlock
Margin="24,0,0,0"
Margin="0,0,0,2"
VerticalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{Binding Selected.Name}"/>
<TextBlock
Margin="24,0,0,2"
VerticalAlignment="Center"
Style="{StaticResource SubtitleTextBlockStyle}"
Text="{Binding Selected.FetterInfo.Title}"/>
</StackPanel>
@@ -286,6 +291,24 @@
Content="{Binding Selected.Property, Mode=OneWay}"
ContentTemplate="{StaticResource PropertyDataTemplate}"/>
<TextBlock
Margin="16,32,0,0"
Style="{StaticResource BaseTextBlockStyle}"
Text="养成材料"/>
<GridView
Margin="16,16,0,0"
ItemsPanel="{StaticResource HorizontalStackPanelTemplate}"
ItemsSource="{Binding Selected.CultivationItemsView}"
SelectionMode="None">
<ItemsControl.ItemTemplate>
<DataTemplate>
<shvc:BottomTextControl Text="{Binding Name}">
<shvc:ItemIcon Icon="{Binding Icon, Converter={StaticResource ItemIconConverter}}" Quality="{Binding RankLevel}"/>
</shvc:BottomTextControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</GridView>
<TextBlock
Margin="16,32,0,0"
Style="{StaticResource BaseTextBlockStyle}"
@@ -311,8 +334,6 @@
Text="搭配角色"/>
<GridView
Margin="16,16,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding Selected.Collocation.Avatars}"
SelectionMode="None">
<GridView.ItemTemplate>
@@ -405,12 +426,7 @@
Margin="16,32,0,0"
Style="{StaticResource BaseTextBlockStyle}"
Text="其他"/>
<Border
Margin="16,16,0,0"
Background="{StaticResource CardBackgroundFillColorDefault}"
BorderBrush="{StaticResource CardStrokeColorDefault}"
BorderThickness="1"
CornerRadius="{StaticResource CompatCornerRadius}">
<Border Margin="16,16,0,0" Style="{StaticResource BorderCardStyle}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
@@ -443,7 +459,7 @@
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Header="料理">
<Grid>
<Grid DataContext="{Binding Selected.CookBonusView}">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
@@ -451,7 +467,6 @@
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition/>
@@ -465,8 +480,8 @@
Grid.Row="1"
Grid.Column="0"
Margin="16,16,0,16"
Text="{Binding Selected.FetterInfo.CookBonus.Name}">
<shvc:ItemIcon Icon="{Binding Selected.FetterInfo.CookBonus.Icon, Converter={StaticResource ItemIconConverter}}" Quality="{Binding Selected.FetterInfo.CookBonus.RankLevel}"/>
Text="{Binding Item.Name}">
<shvc:ItemIcon Icon="{Binding Item.Icon, Converter={StaticResource ItemIconConverter}}" Quality="{Binding Item.RankLevel}"/>
</shvc:BottomTextControl>
<TextBlock
Grid.Column="1"
@@ -477,48 +492,26 @@
Grid.Row="1"
Grid.Column="1"
Margin="16,16,0,16"
Text="{Binding Selected.FetterInfo.CookBonus.OriginName}">
<shvc:ItemIcon Icon="{Binding Selected.FetterInfo.CookBonus.OriginIcon, Converter={StaticResource ItemIconConverter}}" Quality="{Binding Selected.FetterInfo.CookBonus.RankLevel}"/>
Text="{Binding OriginItem.Name}">
<shvc:ItemIcon Icon="{Binding OriginItem.Icon, Converter={StaticResource ItemIconConverter}}" Quality="{Binding OriginItem.RankLevel}"/>
</shvc:BottomTextControl>
<Rectangle
Grid.RowSpan="2"
<StackPanel
Grid.RowSpan="4"
Grid.Column="2"
Width="1"
Margin="16,16,0,16"
Fill="{StaticResource SystemChromeHighColor}"/>
<TextBlock
Grid.Column="3"
Margin="16,16,0,0"
Style="{StaticResource BaseTextBlockStyle}"
Text="材料"/>
<ItemsControl
Grid.Row="1"
Grid.Column="3"
ItemsPanel="{StaticResource HorizontalStackPanelTemplate}"
ItemsSource="{Binding Selected.FetterInfo.CookBonus.InputList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<shvc:BottomTextControl
Margin="16,16,0,16"
Text="{Binding Count}"
ToolTipService.ToolTip="{Binding Name}">
<shvc:ItemIcon Icon="{Binding Icon, Converter={StaticResource ItemIconConverter}}" Quality="{Binding RankLevel}"/>
</shvc:BottomTextControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<TextBlock
Grid.Row="2"
Grid.ColumnSpan="4"
Margin="16,0,16,16"
Text="{Binding Selected.FetterInfo.CookBonus.Description}"
TextWrapping="Wrap"/>
<TextBlock
Grid.Row="3"
Grid.ColumnSpan="4"
Margin="16,0,16,16"
Text="{Binding Selected.FetterInfo.CookBonus.EffectDescription}"
TextWrapping="Wrap"/>
Margin="16">
<TextBlock
Grid.Row="2"
Grid.ColumnSpan="4"
Text="{Binding Item.Description}"
TextWrapping="Wrap"/>
<TextBlock
Grid.Row="3"
Grid.ColumnSpan="4"
Margin="0,16,0,0"
Text="{Binding Item.EffectDescription}"
TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</Expander>
<!-- 衣装 -->

View File

@@ -185,6 +185,37 @@ internal class SettingViewModel : ObservableObject
/// </summary>
public ICommand ShowSignInWebViewDialogCommand { get; }
private static async Task DangerousUnusedLoginMethodAsync()
{
LoginMihoyoBBSDialog dialog = ActivatorUtilities.CreateInstance<LoginMihoyoBBSDialog>(Ioc.Default);
(bool isOk, Dictionary<string, string>? data) = await dialog.GetInputAccountPasswordAsync().ConfigureAwait(false);
if (isOk)
{
(Response<LoginResult>? resp, Aigis? aigis) = await Ioc.Default
.GetRequiredService<PassportClient2>()
.LoginByPasswordAsync(data, CancellationToken.None)
.ConfigureAwait(false);
if (resp != null)
{
if (resp.IsOk())
{
Cookie cookie = Cookie.FromLoginResult(resp.Data);
await Ioc.Default
.GetRequiredService<IUserService>()
.ProcessInputCookieAsync(cookie)
.ConfigureAwait(false);
}
if (resp.ReturnCode == (int)KnownReturnCode.RET_NEED_AIGIS)
{
}
}
}
}
private async Task SetGamePathAsync()
{
IGameLocator locator = Ioc.Default.GetRequiredService<IEnumerable<IGameLocator>>()
@@ -221,33 +252,10 @@ internal class SettingViewModel : ObservableObject
private async Task DebugThrowExceptionAsync()
{
#if DEBUG
LoginMihoyoBBSDialog dialog = ActivatorUtilities.CreateInstance<LoginMihoyoBBSDialog>(Ioc.Default);
(bool isOk, Dictionary<string, string>? data) = await dialog.GetInputAccountPasswordAsync().ConfigureAwait(false);
if (isOk)
{
(Response<LoginResult>? resp, Aigis? aigis) = await Ioc.Default
.GetRequiredService<PassportClient2>()
.LoginByPasswordAsync(data, CancellationToken.None)
.ConfigureAwait(false);
if (resp != null)
{
if (resp.IsOk())
{
Cookie cookie = Cookie.FromLoginResult(resp.Data);
await Ioc.Default
.GetRequiredService<IUserService>()
.ProcessInputCookieAsync(cookie)
.ConfigureAwait(false);
}
if (resp.ReturnCode == (int)KnownReturnCode.RET_NEED_AIGIS)
{
}
}
}
CommunityGameRecordDialog dialog = ActivatorUtilities.CreateInstance<CommunityGameRecordDialog>(Ioc.Default);
await dialog.ShowAsync();
#else
await Task.Yield();
#endif
}
}

View File

@@ -10,6 +10,7 @@ using Snap.Hutao.Factory.Abstraction;
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 @@ internal class WikiAvatarViewModel : ObservableObject
{
if (await metadataService.InitializeAsync().ConfigureAwait(false))
{
Dictionary<MaterialId, Material> idMaterialMap = await metadataService.GetIdToMaterialMapAsync().ConfigureAwait(false);
List<Avatar> avatars = await metadataService.GetAvatarsAsync().ConfigureAwait(false);
List<Avatar> 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 @@ internal class WikiAvatarViewModel : ObservableObject
}
}
private async Task CombineWithAvatarCollocationsAsync(List<Avatar> avatars)
private async Task CombineComplexDataAsync(List<Avatar> avatars, Dictionary<MaterialId, Material> idMaterialMap)
{
if (await hutaoCache.InitializeForWikiAvatarViewModelAsync().ConfigureAwait(false))
{
@@ -112,6 +114,8 @@ internal class WikiAvatarViewModel : ObservableObject
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();
}
}
}

View File

@@ -311,6 +311,14 @@ internal static class ApiEndpoints
public const string AccountCreateActionTicket = $"{PassportApi}/account/ma-cn-verifier/app/createActionTicketByToken";
#endregion
#region Patcher
/// <summary>
/// 胡桃检查更新
/// </summary>
public const string PatcherHutaoStable = $"{PatcherApi}/hutao/stable";
#endregion
#region SdkStaticLauncherApi
/// <summary>
@@ -360,6 +368,8 @@ internal static class ApiEndpoints
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";

View File

@@ -48,7 +48,7 @@ public class MiHoYoJSInterface
/// </summary>
/// <param name="webView">webview2</param>
/// <param name="serviceProvider">服务提供器</param>
protected MiHoYoJSInterface(CoreWebView2 webView, IServiceProvider serviceProvider)
public MiHoYoJSInterface(CoreWebView2 webView, IServiceProvider serviceProvider)
{
this.webView = webView;
this.serviceProvider = serviceProvider;
@@ -159,7 +159,7 @@ public class MiHoYoJSInterface
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 class MiHoYoJSInterface
return new() { Data = new() { { "statusBarHeight", 0 } } };
}
[JsMethod("pushPage")]
public virtual IJsResult? PushPage(JsParam<PushPagePayload> param)
{
webView.Navigate(param.Payload.Page);
return null;
}
[JsMethod("showAlertDialog")]
public virtual Task<IJsResult?> ShowAlertDialogAsync(JsParam param)
{
@@ -286,12 +293,6 @@ public class MiHoYoJSInterface
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 @@ public class MiHoYoJSInterface
private async void OnWebMessageReceived(CoreWebView2 webView2, CoreWebView2WebMessageReceivedEventArgs args)
{
string message = args.TryGetWebMessageAsString();
logger.LogInformation("[OnRawMessage]\n{message}", message);
JsParam param = JsonSerializer.Deserialize<JsParam>(message)!;
logger.LogInformation("[OnMessage]\nMethod : {method}\nPayload : {payload}\nCallback: {callback}", param.Method, param.Payload, param.Callback);
@@ -363,6 +364,7 @@ public class MiHoYoJSInterface
"getUserInfo" => GetUserInfo(param),
"hideLoading" => null,
"login" => null,
"pushPage" => PushPage(param),
"showLoading" => null,
_ => logger.LogWarning<IJsResult>("Unhandled Message Type: {method}", param.Method),
};

View File

@@ -12,11 +12,30 @@ public class DynamicSecrect2Playload
/// q
/// </summary>
[JsonPropertyName("query")]
public Dictionary<string, string> Query { get; set; } = default!;
public Dictionary<string, JsonElement> Query { get; set; } = default!;
/// <summary>
/// b
/// </summary>
[JsonPropertyName("body")]
public string Body { get; set; } = default!;
/// <summary>
/// 获取排序后的的查询参数
/// </summary>
/// <returns>查询参数</returns>
public string GetQueryParam()
{
IEnumerable<string> 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);
}
}

View File

@@ -63,4 +63,4 @@ public class JsParam<TPayload>
Callback = jsParam.Callback,
};
}
}
}

View File

@@ -0,0 +1,16 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Bridge.Model;
/// <summary>
/// 导航页面参数
/// </summary>
public class PushPagePayload
{
/// <summary>
/// 页面Url
/// </summary>
[JsonPropertyName("page")]
public string Page { get; set; } = default!;
}

View File

@@ -59,6 +59,7 @@ internal class BindingClient
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[Obsolete("Set-Cookie 冲突")]
[ApiInformation(Cookie = CookieType.Ltoken)]
public async Task<List<UserGameRole>> GetUserGameRolesByCookieAsync(User user, CancellationToken token = default)
{

View File

@@ -41,12 +41,12 @@ internal class BindingClient2
/// <param name="user">用户</param>
/// <param name="token">取消令牌</param>
/// <returns>用户角色信息</returns>
[ApiInformation(Cookie = CookieType.Stoken)]
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.LK2)]
public async Task<List<UserGameRole>> GetUserGameRolesByStokenAsync(User user, CancellationToken token = default)
{
Response<ListWrapper<UserGameRole>>? resp = await httpClient
.SetUser(user, CookieType.Stoken)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true)
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.LK2, true)
.TryCatchGetFromJsonAsync<Response<ListWrapper<UserGameRole>>>(ApiEndpoints.UserGameRolesByStoken, options, logger, token)
.ConfigureAwait(false);

View File

@@ -19,7 +19,6 @@ namespace Snap.Hutao.Web.Hutao;
/// <summary>
/// 胡桃API客户端
/// </summary>
// [Injection(InjectAs.Transient)]
[HttpClient(HttpClientConfigration.Default)]
internal class HomaClient
{
@@ -220,4 +219,4 @@ internal class HomaClient
{
return httpClient.TryCatchPostAsJsonAsync<SimpleRecord, Response<string>>($"{HutaoAPI}/Record/Upload", playerRecord, options, logger, token);
}
}
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
namespace Snap.Hutao.Web.Hutao.Patch;
/// <summary>
/// 更新信息
/// </summary>
public class PatchInformation
{
/// <summary>
/// 标签名 通常被替换为版本
/// </summary>
public string TagName { get; set; } = default!;
/// <summary>
/// 更新日志
/// </summary>
public string Body { get; set; } = default!;
/// <summary>
/// 浏览器下载链接
/// </summary>
public Uri BrowserDownloadUrl { get; set; } = default!;
/// <summary>
/// 缓存时间
/// </summary>
public DateTimeOffset CacheTime { get; set; }
}

View File

@@ -0,0 +1,51 @@
// Copyright (c) DGP Studio. All rights reserved.
// Licensed under the MIT license.
using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient;
using Snap.Hutao.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;
/// <summary>
/// 更新客户端
/// </summary>
[HttpClient(HttpClientConfigration.Default)]
internal class PatchClient
{
private readonly HttpClient httpClient;
private readonly JsonSerializerOptions options;
private readonly ILogger<HomaClient> logger;
/// <summary>
/// 构造一个新的更新客户端
/// </summary>
/// <param name="httpClient">http 客户端</param>
/// <param name="options">json选项</param>
/// <param name="logger">日志器</param>
public PatchClient(HttpClient httpClient, JsonSerializerOptions options, ILogger<HomaClient> logger)
{
this.httpClient = httpClient;
this.options = options;
this.logger = logger;
}
/// <summary>
/// 更新信息
/// </summary>
/// <param name="token"></param>
/// <returns></returns>
public Task<Patch.PatchInformation?> GetPatchInformationAsync(CancellationToken token = default)
{
return httpClient.TryCatchGetFromJsonAsync<Patch.PatchInformation>(ApiEndpoints.PatcherHutaoStable, options, logger, token);
}
}