diff --git a/src/Snap.Hutao/.editorconfig b/src/Snap.Hutao/.editorconfig index fe0e366b11..a5fa13df09 100644 --- a/src/Snap.Hutao/.editorconfig +++ b/src/Snap.Hutao/.editorconfig @@ -102,8 +102,8 @@ dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case -dotnet_diagnostic.SA1629.severity = silent -dotnet_diagnostic.SA1642.severity = silent +dotnet_diagnostic.SA1629.severity = none +dotnet_diagnostic.SA1642.severity = none dotnet_diagnostic.IDE0060.severity = none diff --git a/src/Snap.Hutao/SettingsUI/SettingsUI.csproj b/src/Snap.Hutao/SettingsUI/SettingsUI.csproj index 630184f023..680dff53b0 100644 --- a/src/Snap.Hutao/SettingsUI/SettingsUI.csproj +++ b/src/Snap.Hutao/SettingsUI/SettingsUI.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs index 1de5db2e8e..de168e725f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs +++ b/src/Snap.Hutao/Snap.Hutao/Core/CoreEnvironment.cs @@ -5,11 +5,7 @@ using Snap.Hutao.Core.Convert; using Snap.Hutao.Core.Json; using Snap.Hutao.Extension; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; -using System.Collections.Immutable; -using System.Text.Encodings.Web; using System.Text.Json.Serialization.Metadata; -using System.Text.Unicode; using Windows.ApplicationModel; namespace Snap.Hutao.Core; diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.Designer.cs new file mode 100644 index 0000000000..d4edeb9e79 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.Designer.cs @@ -0,0 +1,286 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Snap.Hutao.Context.Database; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20221118095755_SplitStoken")] + partial class SplitStoken + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.0"); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("Current") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("achievements"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("achievement_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Info") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("avatar_infos"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ShowInHomeWidget") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("gacha_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("GachaType") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("QueryType") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("gacha_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttachUid") + .HasColumnType("TEXT"); + + b.Property("MihoyoSDK") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("game_accounts"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("settings"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Cookie") + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Stoken") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.cs new file mode 100644 index 0000000000..5b9dfb99d7 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.cs @@ -0,0 +1,29 @@ +// +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + /// + public partial class SplitStoken : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Stoken", + table: "users", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Stoken", + table: "users"); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.Designer.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.Designer.cs new file mode 100644 index 0000000000..706b58d816 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.Designer.cs @@ -0,0 +1,292 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Snap.Hutao.Context.Database; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + [DbContext(typeof(AppDbContext))] + [Migration("20221118124745_AddAidMid")] + partial class AddAidMid + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.0"); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("Current") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("achievements"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("achievement_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Info") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("avatar_infos"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("DailyNote") + .HasColumnType("TEXT"); + + b.Property("DailyTaskNotify") + .HasColumnType("INTEGER"); + + b.Property("DailyTaskNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotify") + .HasColumnType("INTEGER"); + + b.Property("ExpeditionNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("HomeCoinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("ResinNotifyThreshold") + .HasColumnType("INTEGER"); + + b.Property("ShowInHomeWidget") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotify") + .HasColumnType("INTEGER"); + + b.Property("TransformerNotifySuppressed") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("UserId"); + + b.ToTable("daily_notes"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Uid") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("gacha_archives"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ArchiveId") + .HasColumnType("TEXT"); + + b.Property("GachaType") + .HasColumnType("INTEGER"); + + b.Property("Id") + .HasColumnType("INTEGER"); + + b.Property("ItemId") + .HasColumnType("INTEGER"); + + b.Property("QueryType") + .HasColumnType("INTEGER"); + + b.Property("Time") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.HasIndex("ArchiveId"); + + b.ToTable("gacha_items"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttachUid") + .HasColumnType("TEXT"); + + b.Property("MihoyoSDK") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.HasKey("InnerId"); + + b.ToTable("game_accounts"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b => + { + b.Property("Key") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("Key"); + + b.ToTable("settings"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b => + { + b.Property("InnerId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Aid") + .HasColumnType("TEXT"); + + b.Property("Cookie") + .HasColumnType("TEXT"); + + b.Property("IsSelected") + .HasColumnType("INTEGER"); + + b.Property("Mid") + .HasColumnType("TEXT"); + + b.Property("Stoken") + .HasColumnType("TEXT"); + + b.HasKey("InnerId"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => + { + b.HasOne("Snap.Hutao.Model.Entity.AchievementArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b => + { + b.HasOne("Snap.Hutao.Model.Entity.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b => + { + b.HasOne("Snap.Hutao.Model.Entity.GachaArchive", "Archive") + .WithMany() + .HasForeignKey("ArchiveId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Archive"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.cs new file mode 100644 index 0000000000..25e4501637 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.cs @@ -0,0 +1,39 @@ +// +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Snap.Hutao.Migrations +{ + /// + public partial class AddAidMid : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Aid", + table: "users", + type: "TEXT", + nullable: true); + + migrationBuilder.AddColumn( + name: "Mid", + table: "users", + type: "TEXT", + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Aid", + table: "users"); + + migrationBuilder.DropColumn( + name: "Mid", + table: "users"); + } + } +} diff --git a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs index 1f24f21b80..94f9b86482 100644 --- a/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs +++ b/src/Snap.Hutao/Snap.Hutao/Migrations/AppDbContextModelSnapshot.cs @@ -15,7 +15,7 @@ partial class AppDbContextModelSnapshot : ModelSnapshot protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "6.0.10"); + modelBuilder.HasAnnotation("ProductVersion", "7.0.0"); modelBuilder.Entity("Snap.Hutao.Model.Entity.Achievement", b => { @@ -231,12 +231,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) .ValueGeneratedOnAdd() .HasColumnType("TEXT"); + b.Property("Aid") + .HasColumnType("TEXT"); + b.Property("Cookie") .HasColumnType("TEXT"); b.Property("IsSelected") .HasColumnType("INTEGER"); + b.Property("Mid") + .HasColumnType("TEXT"); + + b.Property("Stoken") + .HasColumnType("TEXT"); + b.HasKey("InnerId"); b.ToTable("users"); diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs index 35f8a4a44c..6d6bd8c1aa 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Binding/User/User.cs @@ -5,6 +5,7 @@ using Snap.Hutao.Extension; using Snap.Hutao.Web.Hoyolab; using Snap.Hutao.Web.Hoyolab.Bbs.User; +using Snap.Hutao.Web.Hoyolab.Passport; using Snap.Hutao.Web.Hoyolab.Takumi.Binding; using EntityUser = Snap.Hutao.Model.Entity.User; @@ -57,21 +58,28 @@ public bool IsSelected /// public Cookie? Cookie + { + get => inner.Cookie; + set => inner.Cookie = value; + } + + /// + public Cookie? Stoken { get => inner.Cookie; set { - inner.Cookie = value; - OnPropertyChanged(nameof(HasSToken)); + inner.Stoken = value; + OnPropertyChanged(nameof(HasStoken)); } } /// /// 是否拥有 SToken /// - public bool HasSToken + public bool HasStoken { - get => inner.Cookie!.ContainsSToken(); + get => inner.Stoken != null; } /// @@ -88,14 +96,12 @@ public bool HasSToken /// 从数据库恢复用户 /// /// 数据库实体 - /// 用户客户端 - /// 角色客户端 /// 取消令牌 /// 用户是否初始化完成,若Cookie失效会返回 - internal static async Task ResumeAsync(EntityUser inner, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default) + internal static async Task ResumeAsync(EntityUser inner, CancellationToken token = default) { User user = new(inner); - bool successful = await user.InitializeCoreAsync(userClient, userGameRoleClient, token).ConfigureAwait(false); + bool successful = await user.InitializeCoreAsync(token).ConfigureAwait(false); return successful ? user : null; } @@ -103,40 +109,54 @@ public bool HasSToken /// 创建并初始化用户 /// /// cookie - /// 用户客户端 - /// 角色客户端 /// 取消令牌 /// 用户是否初始化完成,若Cookie失效会返回 - internal static async Task CreateAsync(Cookie cookie, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default) + internal static async Task CreateAsync(Cookie cookie, CancellationToken token = default) { - User user = new(EntityUser.Create(cookie)); - bool successful = await user.InitializeCoreAsync(userClient, userGameRoleClient, token).ConfigureAwait(false); - return successful ? user : null; + EntityUser entity = EntityUser.Create(cookie); + + UserInformation? userInfo = await Ioc.Default + .GetRequiredService() + .VerifyLtokenAsync(cookie, CancellationToken.None) + .ConfigureAwait(false); + + entity.Aid = userInfo?.Aid; + entity.Mid = userInfo?.Mid; + + if (entity.Aid == null && entity.Mid == null) + { + return null; + } + else + { + User user = new(entity); + bool initialized = await user.InitializeCoreAsync(token).ConfigureAwait(false); + return initialized ? user : null; + } } /// /// 更新SToken /// - /// uid - /// cookie - internal void UpdateSToken(string uid, Cookie cookie) + /// cookie + internal void UpdateSToken(Cookie stoken) { - Cookie!.InsertSToken(uid, cookie); - OnPropertyChanged(nameof(HasSToken)); + Stoken = stoken; + OnPropertyChanged(nameof(HasStoken)); } - private async Task InitializeCoreAsync(UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default) + private async Task InitializeCoreAsync(CancellationToken token = default) { if (isInitialized) { return true; } - UserInfo = await userClient + UserInfo = await Ioc.Default.GetRequiredService() .GetUserFullInfoAsync(Entity, token) .ConfigureAwait(false); - UserGameRoles = await userGameRoleClient + UserGameRoles = await Ioc.Default.GetRequiredService() .GetUserGameRolesByCookieAsync(Entity, token) .ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/UserConfiguration.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/UserConfiguration.cs index 69964c1e33..3897b510bf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/UserConfiguration.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/Configuration/UserConfiguration.cs @@ -18,7 +18,13 @@ public void Configure(EntityTypeBuilder builder) builder.Property(e => e.Cookie) .HasColumnType("TEXT") .HasConversion( - e => e == null ? string.Empty : e.ToString(), + e => e!.ToString(), + e => Cookie.Parse(e)); + + builder.Property(e => e.Stoken) + .HasColumnType("TEXT") + .HasConversion( + e => e!.ToString(), e => Cookie.Parse(e)); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs b/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs index 1712bfda37..80d10dae1a 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Entity/User.cs @@ -21,16 +21,31 @@ public class User : ISelectable [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public Guid InnerId { get; set; } + /// + /// 米游社账号 Id + /// + public string? Aid { get; set; } + + /// + /// 米哈游 Id + /// + public string? Mid { get; set; } + /// /// 是否被选中 /// public bool IsSelected { get; set; } /// - /// 用户的Cookie + /// 用户的 Cookie /// public Cookie? Cookie { get; set; } + /// + /// 用户的 Stoken V2 + /// + public Cookie? Stoken { get; set; } + /// /// 创建一个新的用户 /// diff --git a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs index 80feeedc86..afa1e91791 100644 --- a/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs +++ b/src/Snap.Hutao/Snap.Hutao/Model/Primitive/AvatarId.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. using Snap.Hutao.Model.Primitive.Converter; -using Windows.ApplicationModel.Chat; namespace Snap.Hutao.Model.Primitive; diff --git a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs index 152e52949b..7efc1f633b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/GachaLog/UrlProvider/GachaLogUrlStokenProvider.cs @@ -37,7 +37,7 @@ public async Task> GetQueryAsync() Model.Binding.User.User? user = userService.Current; if (user != null && user.SelectedUserGameRole != null) { - if (user.Cookie!.ContainsSToken()) + if (user.HasStoken) { PlayerUid uid = (PlayerUid)user.SelectedUserGameRole; GenAuthKeyData data = GenAuthKeyData.CreateForWebViewGacha(uid); @@ -54,7 +54,7 @@ public async Task> GetQueryAsync() } else { - return new(false, "当前用户的Cookie不包含 Stoken"); + return new(false, "当前用户的 Cookie 不包含 Stoken"); } } else diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs index f8bfac3aa5..a0c334e36e 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserHelper.cs @@ -1,6 +1,7 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. +using Snap.Hutao.Web.Hoyolab; using System.Collections.ObjectModel; using BindingUser = Snap.Hutao.Model.Binding.User.User; @@ -15,13 +16,12 @@ internal static class UserHelper /// 尝试获取用户 /// /// 待查找的用户集合 - /// uid + /// 米哈游Id /// 用户 /// 是否存在用户 - public static bool TryGetUserByUid(ObservableCollection users, string uid, [NotNullWhen(true)] out BindingUser? user) + public static bool TryGetUser(ObservableCollection users, string mid, [NotNullWhen(true)] out BindingUser? user) { - user = users.SingleOrDefault(u => u.UserInfo!.Uid == uid); - + user = users.SingleOrDefault(u => u.Entity.Mid == mid); return user != null; } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs index 2d9d78495f..9b8b09c819 100644 --- a/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs +++ b/src/Snap.Hutao/Snap.Hutao/Service/User/UserService.cs @@ -114,13 +114,11 @@ public async Task> GetUserCollectionAsync() using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - UserClient userClient = scope.ServiceProvider.GetRequiredService(); - BindingClient bindingClient = scope.ServiceProvider.GetRequiredService(); foreach (Model.Entity.User entity in appDbContext.Users) { BindingUser? initialized = await BindingUser - .ResumeAsync(entity, userClient, bindingClient) + .ResumeAsync(entity) .ConfigureAwait(false); if (initialized != null) @@ -177,51 +175,44 @@ public async Task> GetUserCollectionAsync() /// public async Task> ProcessInputCookieAsync(Cookie cookie) { - cookie.Trim(); Must.NotNull(userCollection!); - // 检查 uid 是否存在 - if (cookie.TryGetUid(out string? uid)) + string? mid = await cookie.GetMidAsync().ConfigureAwait(false); + + if (mid == null) { - // 检查 login ticket 是否存在 - // 若存在则尝试升级至 stoken - await cookie.TryAddMultiTokenAsync(uid).ConfigureAwait(false); + return new(UserOptionResult.Invalid, "输入的Cookie无法获取用户信息"); + } - // 检查 uid 对应用户是否存在 - if (UserHelper.TryGetUserByUid(userCollection, uid, out BindingUser? userWithSameUid)) + // 检查 mid 对应用户是否存在 + if (UserHelper.TryGetUser(userCollection, mid, out BindingUser? user)) + { + using (IServiceScope scope = scopeFactory.CreateScope()) { - using (IServiceScope scope = scopeFactory.CreateScope()) - { - AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); + AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - // 检查 stoken 是否存在 - if (cookie.ContainsSToken()) - { - // insert stoken - await ThreadHelper.SwitchToMainThreadAsync(); - userWithSameUid.UpdateSToken(uid, cookie); - appDbContext.Users.UpdateAndSave(userWithSameUid.Entity); + // 检查 stoken 是否存在 + if (user.HasStoken) + { + // update stoken + await ThreadHelper.SwitchToMainThreadAsync(); + user.UpdateSToken(cookie); + appDbContext.Users.UpdateAndSave(user.Entity); - return new(UserOptionResult.Upgraded, uid); - } + return new(UserOptionResult.Upgraded, mid); + } - if (cookie.ContainsLTokenAndCookieToken()) - { - await ThreadHelper.SwitchToMainThreadAsync(); - userWithSameUid.Cookie = cookie; - appDbContext.Users.UpdateAndSave(userWithSameUid.Entity); + await ThreadHelper.SwitchToMainThreadAsync(); + user.Cookie = cookie; + appDbContext.Users.UpdateAndSave(user.Entity); - return new(UserOptionResult.Updated, uid); - } - } - } - else if (cookie.ContainsLTokenAndCookieToken()) - { - return await TryCreateUserAndAddAsync(cookie).ConfigureAwait(false); + return new(UserOptionResult.Updated, mid); } } - - return new(UserOptionResult.Incomplete, null!); + else + { + return await TryCreateUserAndAddAsync(cookie).ConfigureAwait(false); + } } private async Task> TryCreateUserAndAddAsync(Cookie cookie) @@ -229,10 +220,8 @@ private async Task> TryCreateUserAndAddAsy using (IServiceScope scope = scopeFactory.CreateScope()) { AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService(); - UserClient userClient = scope.ServiceProvider.GetRequiredService(); - BindingClient bindingClient = scope.ServiceProvider.GetRequiredService(); - BindingUser? newUser = await BindingUser.CreateAsync(cookie, userClient, bindingClient).ConfigureAwait(false); + BindingUser? newUser = await BindingUser.CreateAsync(cookie).ConfigureAwait(false); if (newUser != null) { // Sync cache @@ -256,7 +245,7 @@ private async Task> TryCreateUserAndAddAsy } else { - return new(UserOptionResult.Invalid, null!); + return new(UserOptionResult.Invalid, "输入的Cookie无法获取用户信息"); } } } diff --git a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml index bd5aebfa7c..fdb656f58e 100644 --- a/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml +++ b/src/Snap.Hutao/Snap.Hutao/View/UserView.xaml @@ -127,7 +127,7 @@ @@ -210,7 +210,7 @@ - + diff --git a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs index 8a0cbfc77d..61b4594f33 100644 --- a/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs +++ b/src/Snap.Hutao/Snap.Hutao/ViewModel/UserViewModel.cs @@ -142,7 +142,7 @@ private async Task AddUserAsync() private async Task LoginMihoyoBBS2Async() { MainWindow mainWindow = Ioc.Default.GetRequiredService(); - await new LoginMihoyoBBSDialog(mainWindow).GetInputAccountPasswordAsync(); + await new LoginMihoyoBBSDialog(mainWindow).GetInputAccountPasswordAsync().ConfigureAwait(false); } private void LoginMihoyoBBS() diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs index 798f2990a1..894792569b 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Bbs/User/UserClient.cs @@ -3,7 +3,6 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Web.Hoyolab.Annotation; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Response; using System.Net.Http; @@ -43,7 +42,7 @@ public UserClient(HttpClient httpClient, JsonSerializerOptions options, ILogger< { Response? resp = await httpClient .SetUser(user, CookieType.Cookie) - //.SetReferer(ApiEndpoints.BbsReferer) + .SetReferer(ApiEndpoints.BbsReferer) .TryCatchGetFromJsonAsync>(ApiEndpoints.UserFullInfo, options, logger, token) .ConfigureAwait(false); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs index 29438b661e..d512cf1908 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.Constant.cs @@ -10,19 +10,7 @@ namespace Snap.Hutao.Web.Hoyolab; [SuppressMessage("", "SA1600")] public partial class Cookie { - public const string ACCOUNT_ID = "account_id"; - public const string COOKIE_TOKEN = "cookie_token"; - - public const string E_HK4E_TOKEN = "e_hk4e_token"; - - public const string LOGIN_TICKET = "login_ticket"; - public const string LOGIN_UID = "login_uid"; - - public const string LTOKEN = "ltoken"; - public const string LTUID = "ltuid"; - public const string MID = "mid"; - public const string STOKEN = "stoken"; public const string STUID = "stuid"; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs index ea6107c078..6d79fd35a2 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Cookie.cs @@ -3,6 +3,7 @@ using Microsoft.Web.WebView2.Core; using Snap.Hutao.Extension; +using Snap.Hutao.Web.Hoyolab.Passport; using Snap.Hutao.Web.Hoyolab.Takumi.Auth; namespace Snap.Hutao.Web.Hoyolab; @@ -65,152 +66,52 @@ public static Cookie FromCoreWebView2Cookies(IReadOnlyList w } /// - /// 存在 LoginTicket + /// 此 Cookie 是 SToken /// /// 是否存在 - public bool ContainsLoginTicket() + public bool IsStoken() { - return inner.ContainsKey(LOGIN_TICKET); - } - - /// - /// 存在 LToken 与 CookieToken - /// - /// 是否存在 - public bool ContainsLTokenAndCookieToken() - { - return inner.ContainsKey(LTOKEN) && inner.ContainsKey(COOKIE_TOKEN); - } - - /// - /// 存在 SToken - /// - /// 是否存在 - public bool ContainsSToken() - { - return inner.ContainsKey(STOKEN); - } - - /// - /// 插入 Stoken - /// - /// stuid - /// cookie - public void InsertSToken(string stuid, Cookie cookie) - { - inner[STUID] = stuid; - inner[STOKEN] = cookie.inner[STOKEN]; - } + int stokenFlag = 0; - /// - /// 移除无效的键 - /// - public void Trim() - { - foreach (string key in inner.Keys.ToList()) + foreach (string key in inner.Keys) { - if (key == ACCOUNT_ID - || key == COOKIE_TOKEN - || key == LOGIN_UID - || key == LOGIN_TICKET - || key == LTUID - || key == LTOKEN - || key == STUID - || key == STOKEN) + if (key is MID or STOKEN or STUID) { - continue; - } - else - { - inner.Remove(key); + stokenFlag++; } } - } - /// - public bool TryGetValue(string key, [NotNullWhen(true)] out string? value) - { - return inner.TryGetValue(key, out value); + return stokenFlag == 3; } - public bool TryGetLoginTicket([NotNullWhen(true)] out string? loginTicket) - { - return inner.TryGetValue(LOGIN_TICKET, out loginTicket); - } - - public bool TryGetUid([NotNullWhen(true)] out string? uid) + /// + /// 异步获取 Mid + /// + /// mid + public async Task GetMidAsync() { - Dictionary uidCounter = new(); - - foreach ((string key, string value) in inner) - { - if (key is ACCOUNT_ID or LOGIN_UID or LTUID or STUID) - { - uidCounter.Increase(key); - } - } - - if (uidCounter.Count > 0) + string? mid; + if (IsStoken()) { - // fix #88 自带页面登录米游社,提示登录失败 - string key = uidCounter.MaxBy(kvp => kvp.Value).Key; - uid = inner[key]; - return true; + mid = inner[MID]; } else { - uid = null; - return false; - } - } - - /// - /// 异步尝试添加MultiToken - /// - /// uid - /// 任务 - public async Task TryAddMultiTokenAsync(string uid) - { - if (TryGetLoginTicket(out string? loginTicket)) - { - // get multitoken - Dictionary multiToken = await Ioc.Default - .GetRequiredService() - .GetMultiTokenByLoginTicketAsync(loginTicket, uid, default) + UserInformation? userInfo = await Ioc.Default + .GetRequiredService() + .VerifyLtokenAsync(this, CancellationToken.None) .ConfigureAwait(false); - if (multiToken.Count >= 2) - { - inner[STUID] = uid; - inner[STOKEN] = multiToken[STOKEN]; - inner[LTUID] = uid; - inner[LTOKEN] = multiToken[LTOKEN]; - - inner.Remove(LOGIN_TICKET); - inner.Remove(LOGIN_UID); - } + mid = userInfo?.Mid; } + + return mid; } - /// - /// 根据类型输出对应的Cookie - /// - /// 类型 - /// Cookie对应的字符串表示 - public string ToString(CookieType type) + /// + public bool TryGetValue(string key, [NotNullWhen(true)] out string? value) { - IEnumerable> results; - - results = type switch - { - CookieType.None => Enumerable.Empty>(), - CookieType.Cookie => inner.Where(kvp => kvp.Key is E_HK4E_TOKEN or LTUID or LTOKEN or ACCOUNT_ID or COOKIE_TOKEN), - CookieType.Stoken => inner.Where(kvp => kvp.Key is STUID or STOKEN or MID), - CookieType.All => inner, - _ => throw Must.NeverHappen(type.ToString()), - }; - - return string.Join(';', results.Select(kvp => $"{kvp.Key}={kvp.Value}")); + return inner.TryGetValue(key, out value); } /// @@ -219,6 +120,6 @@ public string ToString(CookieType type) /// Cookie的字符串表示 public override string ToString() { - return ToString(CookieType.All); + return string.Join(';', inner.Select(kvp => $"{kvp.Key}={kvp.Value}")); } } \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs deleted file mode 100644 index ba42f8faf1..0000000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Core.Convert; -using System.Text; - -namespace Snap.Hutao.Web.Hoyolab.DynamicSecret; - -/// -/// 为MiHoYo接口请求器 提供动态密钥 -/// -internal abstract class DynamicSecretProvider : Md5Convert -{ - private const string RandomRange = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; - - /// - /// 创建动态密钥 - /// - /// SALT 类型 - /// 密钥 - public static string Create(SaltType saltType) - { - Verify.Operation(saltType is SaltType.K2 or SaltType.LK2, "SALT 值无效"); - - // unix timestamp - long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - - string r = GetRandomString(); - - string salt = Core.CoreEnvironment.DynamicSecrets[saltType]; - string check = ToHexString($"salt={salt}&t={t}&r={r}").ToLowerInvariant(); - - return $"{t},{r},{check}"; - } - - private static string GetRandomString() - { - StringBuilder sb = new(6); - - for (int i = 0; i < 6; i++) - { - int pos = Random.Shared.Next(0, RandomRange.Length); - sb.Append(RandomRange[pos]); - } - - return sb.ToString(); - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs deleted file mode 100644 index fbd423cb72..0000000000 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/DynamicSecretProvider2.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) DGP Studio. All rights reserved. -// Licensed under the MIT license. - -using Snap.Hutao.Core.Convert; - -namespace Snap.Hutao.Web.Hoyolab.DynamicSecret; - -/// -/// 为MiHoYo接口请求器 提供2代动态密钥 -/// -internal abstract class DynamicSecretProvider2 : Md5Convert -{ - /// - /// 创建动态密钥 - /// - /// SALT 类型 - /// json格式化器 - /// 查询url - /// 请求体 - /// 密钥 - public static string Create(SaltType saltType, JsonSerializerOptions options, string queryUrl, object? postBody = null) - { - Verify.Operation(saltType is SaltType.X6 or SaltType.X4 or SaltType.PROD, "SALT 值无效"); - - // unix timestamp - long t = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); - - // random - int r = GetRandom(); - - // body - string b = postBody is null ? GetDefaultBody(saltType) : JsonSerializer.Serialize(postBody, options); - - // query - string[] queries = queryUrl.Split('?', 2); - string q = queries.Length == 2 ? string.Join('&', queries[1].Split('&').OrderBy(x => x)) : string.Empty; - - // check - string salt = Core.CoreEnvironment.DynamicSecrets[saltType]; - string check = ToHexString($"salt={salt}&t={t}&r={r}&b={b}&q={q}").ToLowerInvariant(); - - return $"{t},{r},{check}"; - } - - private static string GetDefaultBody(SaltType saltType) - { - return saltType switch - { - SaltType.X4 or SaltType.X6 => string.Empty, - SaltType.PROD => "{}", - _ => throw Must.NeverHappen(((int)saltType).ToString()), - }; - } - - private static int GetRandom() - { - // 原汁原味 - // v16 = time(0LL); - // srand(v16); - // v17 = (int)((double)rand() / 2147483650.0 * 100000.0 + 100000.0) % 1000000; - // if (v17 >= 100001) - // v18 = v17; - // else - // v18 = v17 + 542367; - int rand = Random.Shared.Next(100000, 200000); - return rand == 100000 ? 642367 : rand; - } -} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs index 43206f39b5..e6d2281564 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/DynamicSecret/HttpClientDynamicSecretExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Web.Hoyolab.DynamicSecret.Http; using Snap.Hutao.Web.Request; using System.Net.Http; @@ -25,47 +24,4 @@ public static HttpClient UseDynamicSecret(this HttpClient client, DynamicSecretV client.DefaultRequestHeaders.Set("DS-Option", $"{version}|{salt}|{includeChars}"); return client; } - - /// - /// 使用一代动态密钥执行 GET/POST 操作 - /// - /// 请求器 - /// SALT 类型 - /// 响应 - [Obsolete] - public static HttpClient UsingDynamicSecret1(this HttpClient httpClient, SaltType type) - { - httpClient.DefaultRequestHeaders.Set("DS", DynamicSecretProvider.Create(type)); - return httpClient; - } - - /// - /// 使用二代动态密钥执行 GET/POST 操作 - /// - /// 请求器 - /// SALT 类型 - /// 选项 - /// 地址 - /// 响应 - [Obsolete] - public static IDynamicSecretHttpClient UsingDynamicSecret2(this HttpClient httpClient, SaltType type, JsonSerializerOptions options, string url) - { - return new DynamicSecretHttpClient(httpClient, type, options, url); - } - - /// - /// 使用二代动态密钥执行 GET/POST 操作 - /// - /// 请求数据的类型 - /// 请求器 - /// SALT 类型 - /// 选项 - /// 地址 - /// post数据 - /// 响应 - public static IDynamicSecretHttpClient UsingDynamicSecret2(this HttpClient httpClient, SaltType type, JsonSerializerOptions options, string url, TValue data) - where TValue : class - { - return new DynamicSecretHttpClient(httpClient, type, options, url, data); - } -} +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs index ab8a8ec763..b9cb7345ad 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/HttpClientExtensions.cs @@ -2,8 +2,6 @@ // Licensed under the MIT license. using Snap.Hutao.Core.Logging; -using Snap.Hutao.Model.Binding.User; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Request; using System.Net.Http; using System.Net.Http.Json; @@ -62,7 +60,7 @@ internal static class HttpClientExtensions } /// - /// 设置用户的Cookie + /// 设置用户的 Cookie /// /// http客户端 /// 实体用户 @@ -70,7 +68,25 @@ internal static class HttpClientExtensions /// 客户端 internal static HttpClient SetUser(this HttpClient httpClient, Model.Entity.User user, CookieType cookie) { - httpClient.DefaultRequestHeaders.Set("Cookie", user.Cookie!.ToString(cookie)); + if (cookie == CookieType.Stoken) + { + return httpClient.SetRawCookie(user.Stoken!); + } + else + { + return httpClient.SetRawCookie(user.Cookie!); + } + } + + /// + /// 设置 Cookie + /// + /// http客户端 + /// Cookie + /// 客户端 + internal static HttpClient SetRawCookie(this HttpClient httpClient, Cookie cookie) + { + httpClient.DefaultRequestHeaders.Set("Cookie", cookie.ToString()); return httpClient; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/Link.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/Link.cs index bf321f307f..7c83723692 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/Link.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/Link.cs @@ -1,10 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Passport; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs index c02c3d7ff3..094158d5eb 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient.cs @@ -4,7 +4,6 @@ using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; using Snap.Hutao.Model.Entity; using Snap.Hutao.Web.Hoyolab.Annotation; -using Snap.Hutao.Web.Hoyolab.DynamicSecret; using Snap.Hutao.Web.Response; using System.Net.Http; @@ -36,18 +35,18 @@ public PassportClient(HttpClient httpClient, JsonSerializerOptions options, ILog /// /// 异步验证Ltoken /// - /// 用户 + /// 待校验的 Cookie /// 取消令牌 /// 验证信息 [ApiInformation(Cookie = CookieType.Cookie)] - public async Task VerifyLtokenAsync(User user, CancellationToken token) + public async Task VerifyLtokenAsync(Cookie cookie, CancellationToken token) { - Response? response = await httpClient - .SetUser(user, CookieType.All) - .TryCatchPostAsJsonAsync>(ApiEndpoints.AccountVerifyLtoken, new(), options, logger, token) + Response? response = await httpClient + .SetRawCookie(cookie) + .TryCatchPostAsJsonAsync>(ApiEndpoints.AccountVerifyLtoken, new(), options, logger, token) .ConfigureAwait(false); - return response?.Data; + return response?.Data?.UserInfo; } private class Timestamp diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs index 73d205c43f..102cf69387 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/PassportClient2.cs @@ -50,8 +50,8 @@ public PassportClient2(HttpClient httpClient, JsonSerializerOptions options, ILo }; Response? resp = await httpClient - .UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AccountLoginByPassword, data) - .TryCatchPostAsJsonAsync>(logger, token) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.PROD, true) + .TryCatchPostAsJsonAsync, Response>(ApiEndpoints.AccountLoginByPassword, data, options, logger, token) .ConfigureAwait(false); return resp?.Data; @@ -68,8 +68,8 @@ public PassportClient2(HttpClient httpClient, JsonSerializerOptions options, ILo { Response? resp = await httpClient .SetUser(user, CookieType.Stoken) - .UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AccountCookieAccountInfoBySToken) - .TryCatchGetFromJsonAsync>(logger, token) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.PROD, true) + .TryCatchGetFromJsonAsync>(ApiEndpoints.AccountCookieAccountInfoBySToken, options, logger, token) .ConfigureAwait(false); return resp?.Data; diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInfoWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInfoWrapper.cs new file mode 100644 index 0000000000..080580b291 --- /dev/null +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInfoWrapper.cs @@ -0,0 +1,16 @@ +// Copyright (c) DGP Studio. All rights reserved. +// Licensed under the MIT license. + +namespace Snap.Hutao.Web.Hoyolab.Passport; + +/// +/// 用户信息包装器 +/// +public class UserInfoWrapper +{ + /// + /// 用户信息 + /// + [JsonPropertyName("user_info")] + public UserInformation UserInfo { get; set; } = default!; +} \ No newline at end of file diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInformation.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInformation.cs index ce3872a8e8..8004171b39 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInformation.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Passport/UserInformation.cs @@ -1,10 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Passport; /// @@ -101,4 +97,4 @@ public class UserInformation /// [JsonPropertyName("links")] public List Links { get; set; } = default!; -} \ No newline at end of file +} diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketAccountInfo.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketAccountInfo.cs index e76f894077..1664b55a08 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketAccountInfo.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketAccountInfo.cs @@ -1,11 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Web.Hoyolab.Takumi.Binding; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs index 9ac5e9b612..72760f0adc 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/ActionTicketWrapper.cs @@ -1,11 +1,6 @@ // Copyright (c) DGP Studio. All rights reserved. // Licensed under the MIT license. -using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; -using Snap.Hutao.Web.Hoyolab.Takumi.Binding; -using Snap.Hutao.Web.Response; -using System.Net.Http; - namespace Snap.Hutao.Web.Hoyolab.Takumi.Auth; /// diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs index e8548cd276..8cce8e7599 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/Auth/AuthClient.cs @@ -41,47 +41,20 @@ public AuthClient(HttpClient httpClient, JsonSerializerOptions options, ILogger< /// 操作 /// 用户 /// 操作凭证 - [ApiInformation(Cookie = CookieType.Stoken, Salt = DynamicSecret.SaltType.K2)] + [ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)] public async Task GetActionTicketByStokenAsync(string action, User user) { - if (user.Cookie!.TryGetValue(Cookie.STOKEN, out string? stoken)) + if (user.Stoken != null) { - if (user.Cookie.TryGetUid(out string? uid)) - { - Response? resp = await httpClient - .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true) - .TryCatchGetFromJsonAsync>(ApiEndpoints.AuthActionTicket(action, stoken, uid), options, logger) - .ConfigureAwait(false); + user.Stoken.TryGetValue(Cookie.STOKEN, out string? token); + Response? resp = await httpClient + .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true) + .TryCatchGetFromJsonAsync>(ApiEndpoints.AuthActionTicket(action, token!, user.Aid!), options, logger) + .ConfigureAwait(false); - return resp?.Data?.Ticket; - } + return resp?.Data?.Ticket; } return null; } - - /// - /// 获取 MultiToken - /// - /// 登录票证 - /// uid - /// 取消令牌 - /// 包含token的字典 - [Obsolete] - public async Task> GetMultiTokenByLoginTicketAsync(string loginTicket, string loginUid, CancellationToken token) - { - Response>? resp = await httpClient - .TryCatchGetFromJsonAsync>>(ApiEndpoints.AuthMultiToken(loginTicket, loginUid), options, logger, token) - .ConfigureAwait(false); - - if (resp?.Data != null) - { - Dictionary dict = resp.Data.List.ToDictionary(n => n.Name, n => n.Token); - Must.Argument(dict.ContainsKey(Cookie.LTOKEN), "MultiToken 应该包含 ltoken"); - Must.Argument(dict.ContainsKey(Cookie.STOKEN), "MultiToken 应该包含 stoken"); - return dict; - } - - return new(); - } } \ No newline at end of file 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 858e2f2d9e..df1ead7689 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 @@ -2,6 +2,7 @@ // Licensed under the MIT license. using Snap.Hutao.Core.DependencyInjection.Annotation.HttpClient; +using Snap.Hutao.Extension; using Snap.Hutao.Model.Entity; using Snap.Hutao.Web.Hoyolab.Annotation; using Snap.Hutao.Web.Hoyolab.DynamicSecret; @@ -64,11 +65,11 @@ public async Task> GetUserGameRolesByStokenAsync(User user, C public async Task GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default) { Response? resp = await httpClient - .SetUser(user, CookieType.Stoken) - .SetReferer("https://app.mihoyo.com") - .UsingDynamicSecret1(SaltType.K2) - .TryCatchPostAsJsonAsync>(ApiEndpoints.BindingGenAuthKey, data, options, logger, token) - .ConfigureAwait(false); + .SetUser(user, CookieType.Stoken) + .SetReferer("https://app.mihoyo.com") + .UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true) + .TryCatchPostAsJsonAsync>(ApiEndpoints.BindingGenAuthKey, data, options, logger, token) + .ConfigureAwait(false); return resp?.Data; } diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs index 9a40e2d93f..b0f2148fdf 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hoyolab/Takumi/GameRecord/GameRecordClient.cs @@ -6,7 +6,6 @@ using Snap.Hutao.Model.Entity; using Snap.Hutao.Web.Hoyolab.Annotation; using Snap.Hutao.Web.Hoyolab.DynamicSecret; -using Snap.Hutao.Web.Hoyolab.DynamicSecret.Http; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Avatar; using Snap.Hutao.Web.Hoyolab.Takumi.GameRecord.Widget; using Snap.Hutao.Web.Response; @@ -129,8 +128,8 @@ public async Task> GetCharactersAsync(User user, PlayerUid uid, Response? resp = await httpClient .SetUser(user, CookieType.Cookie) - .UsingDynamicSecret2(SaltType.X4, options, ApiEndpoints.GameRecordCharacter, data) - .TryCatchPostAsJsonAsync>(logger, token) + .UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.X4, false) + .TryCatchPostAsJsonAsync>(ApiEndpoints.GameRecordCharacter, data, options, logger, token) .ConfigureAwait(false); return EnumerableExtension.EmptyIfNull(resp?.Data?.Avatars); diff --git a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs index faf9349a34..f78e73a81f 100644 --- a/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs +++ b/src/Snap.Hutao/Snap.Hutao/Web/Hutao/HomaClient.cs @@ -177,13 +177,8 @@ public async Task GetPlayerRecordAsync(User user, CancellationToke { Must.NotNull(user.SelectedUserGameRole!); - PlayerInfo? playerInfo = await gameRecordClient - .GetPlayerInfoAsync(user.Entity, user.SelectedUserGameRole, token) - .ConfigureAwait(false); - Must.NotNull(playerInfo!); - List characters = await gameRecordClient - .GetCharactersAsync(user.Entity, user.SelectedUserGameRole, playerInfo, token) + .GetCharactersAsync(user.Entity, user.SelectedUserGameRole, token) .ConfigureAwait(false); SpiralAbyss? spiralAbyssInfo = await gameRecordClient