mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
support ltoken requests #207
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221109.1" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.2.221116.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,11 +5,7 @@ using Microsoft.Win32;
|
||||
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;
|
||||
|
||||
286
src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.Designer.cs
generated
Normal file
286
src/Snap.Hutao/Snap.Hutao/Migrations/20221118095755_SplitStoken.Designer.cs
generated
Normal file
@@ -0,0 +1,286 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ShowInHomeWidget")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Cookie")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SplitStoken : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Stoken",
|
||||
table: "users",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Stoken",
|
||||
table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
292
src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.Designer.cs
generated
Normal file
292
src/Snap.Hutao/Snap.Hutao/Migrations/20221118124745_AddAidMid.Designer.cs
generated
Normal file
@@ -0,0 +1,292 @@
|
||||
// <auto-generated />
|
||||
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
|
||||
{
|
||||
/// <inheritdoc />
|
||||
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<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Current")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Status")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("achievements");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AchievementArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("achievement_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.AvatarInfo", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Info")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("avatar_infos");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.DailyNoteEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("DailyNote")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("DailyTaskNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("DailyTaskNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ExpeditionNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("HomeCoinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HomeCoinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ResinNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ResinNotifyThreshold")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("ShowInHomeWidget")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotify")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("TransformerNotifySuppressed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("daily_notes");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaArchive", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Uid")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("gacha_archives");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GachaItem", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<Guid>("ArchiveId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("GachaType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("QueryType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset>("Time")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.HasIndex("ArchiveId");
|
||||
|
||||
b.ToTable("gacha_items");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.GameAccount", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AttachUid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("MihoyoSDK")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("game_accounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.SettingEntry", b =>
|
||||
{
|
||||
b.Property<string>("Key")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Key");
|
||||
|
||||
b.ToTable("settings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Snap.Hutao.Model.Entity.User", b =>
|
||||
{
|
||||
b.Property<Guid>("InnerId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Cookie")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// <auto-generated />
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Snap.Hutao.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddAidMid : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Aid",
|
||||
table: "users",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "Mid",
|
||||
table: "users",
|
||||
type: "TEXT",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Aid",
|
||||
table: "users");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "Mid",
|
||||
table: "users");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Snap.Hutao.Migrations
|
||||
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 @@ namespace Snap.Hutao.Migrations
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Aid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Cookie")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsSelected")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Mid")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Stoken")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("InnerId");
|
||||
|
||||
b.ToTable("users");
|
||||
|
||||
@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
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 class User : ObservableObject
|
||||
|
||||
/// <inheritdoc cref="EntityUser.Cookie"/>
|
||||
public Cookie? Cookie
|
||||
{
|
||||
get => inner.Cookie;
|
||||
set => inner.Cookie = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityUser.Stoken"/>
|
||||
public Cookie? Stoken
|
||||
{
|
||||
get => inner.Cookie;
|
||||
set
|
||||
{
|
||||
inner.Cookie = value;
|
||||
OnPropertyChanged(nameof(HasSToken));
|
||||
inner.Stoken = value;
|
||||
OnPropertyChanged(nameof(HasStoken));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否拥有 SToken
|
||||
/// </summary>
|
||||
public bool HasSToken
|
||||
public bool HasStoken
|
||||
{
|
||||
get => inner.Cookie!.ContainsSToken();
|
||||
get => inner.Stoken != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,14 +96,12 @@ public class User : ObservableObject
|
||||
/// 从数据库恢复用户
|
||||
/// </summary>
|
||||
/// <param name="inner">数据库实体</param>
|
||||
/// <param name="userClient">用户客户端</param>
|
||||
/// <param name="userGameRoleClient">角色客户端</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>用户是否初始化完成,若Cookie失效会返回 <see langword="false"/> </returns>
|
||||
internal static async Task<User?> ResumeAsync(EntityUser inner, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default)
|
||||
internal static async Task<User?> 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 class User : ObservableObject
|
||||
/// 创建并初始化用户
|
||||
/// </summary>
|
||||
/// <param name="cookie">cookie</param>
|
||||
/// <param name="userClient">用户客户端</param>
|
||||
/// <param name="userGameRoleClient">角色客户端</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>用户是否初始化完成,若Cookie失效会返回 <see langword="null"/> </returns>
|
||||
internal static async Task<User?> CreateAsync(Cookie cookie, UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default)
|
||||
internal static async Task<User?> 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<PassportClient>()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新SToken
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <param name="cookie">cookie</param>
|
||||
internal void UpdateSToken(string uid, Cookie cookie)
|
||||
/// <param name="stoken">cookie</param>
|
||||
internal void UpdateSToken(Cookie stoken)
|
||||
{
|
||||
Cookie!.InsertSToken(uid, cookie);
|
||||
OnPropertyChanged(nameof(HasSToken));
|
||||
Stoken = stoken;
|
||||
OnPropertyChanged(nameof(HasStoken));
|
||||
}
|
||||
|
||||
private async Task<bool> InitializeCoreAsync(UserClient userClient, BindingClient userGameRoleClient, CancellationToken token = default)
|
||||
private async Task<bool> InitializeCoreAsync(CancellationToken token = default)
|
||||
{
|
||||
if (isInitialized)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
UserInfo = await userClient
|
||||
UserInfo = await Ioc.Default.GetRequiredService<UserClient>()
|
||||
.GetUserFullInfoAsync(Entity, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
UserGameRoles = await userGameRoleClient
|
||||
UserGameRoles = await Ioc.Default.GetRequiredService<BindingClient>()
|
||||
.GetUserGameRolesByCookieAsync(Entity, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
|
||||
@@ -18,7 +18,13 @@ internal class UserConfiguration : IEntityTypeConfiguration<User>
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -21,16 +21,31 @@ public class User : ISelectable
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid InnerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 米游社账号 Id
|
||||
/// </summary>
|
||||
public string? Aid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 米哈游 Id
|
||||
/// </summary>
|
||||
public string? Mid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否被选中
|
||||
/// </summary>
|
||||
public bool IsSelected { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户的Cookie
|
||||
/// 用户的 Cookie
|
||||
/// </summary>
|
||||
public Cookie? Cookie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户的 Stoken V2
|
||||
/// </summary>
|
||||
public Cookie? Stoken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的用户
|
||||
/// </summary>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Primitive.Converter;
|
||||
using Windows.ApplicationModel.Chat;
|
||||
|
||||
namespace Snap.Hutao.Model.Primitive;
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
|
||||
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 @@ internal class GachaLogUrlStokenProvider : IGachaLogUrlProvider
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(false, "当前用户的Cookie不包含 Stoken");
|
||||
return new(false, "当前用户的 Cookie 不包含 Stoken");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -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
|
||||
/// 尝试获取用户
|
||||
/// </summary>
|
||||
/// <param name="users">待查找的用户集合</param>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <param name="mid">米哈游Id</param>
|
||||
/// <param name="user">用户</param>
|
||||
/// <returns>是否存在用户</returns>
|
||||
public static bool TryGetUserByUid(ObservableCollection<BindingUser> users, string uid, [NotNullWhen(true)] out BindingUser? user)
|
||||
public static bool TryGetUser(ObservableCollection<BindingUser> 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;
|
||||
}
|
||||
}
|
||||
@@ -114,13 +114,11 @@ internal class UserService : IUserService
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
UserClient userClient = scope.ServiceProvider.GetRequiredService<UserClient>();
|
||||
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService<BindingClient>();
|
||||
|
||||
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 @@ internal class UserService : IUserService
|
||||
/// <inheritdoc/>
|
||||
public async Task<ValueResult<UserOptionResult, string>> 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);
|
||||
|
||||
// 检查 uid 对应用户是否存在
|
||||
if (UserHelper.TryGetUserByUid(userCollection, uid, out BindingUser? userWithSameUid))
|
||||
{
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
// 检查 stoken 是否存在
|
||||
if (cookie.ContainsSToken())
|
||||
{
|
||||
// insert stoken
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
userWithSameUid.UpdateSToken(uid, cookie);
|
||||
appDbContext.Users.UpdateAndSave(userWithSameUid.Entity);
|
||||
|
||||
return new(UserOptionResult.Upgraded, uid);
|
||||
}
|
||||
|
||||
if (cookie.ContainsLTokenAndCookieToken())
|
||||
{
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
userWithSameUid.Cookie = cookie;
|
||||
appDbContext.Users.UpdateAndSave(userWithSameUid.Entity);
|
||||
|
||||
return new(UserOptionResult.Updated, uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cookie.ContainsLTokenAndCookieToken())
|
||||
{
|
||||
return await TryCreateUserAndAddAsync(cookie).ConfigureAwait(false);
|
||||
}
|
||||
return new(UserOptionResult.Invalid, "输入的Cookie无法获取用户信息");
|
||||
}
|
||||
|
||||
return new(UserOptionResult.Incomplete, null!);
|
||||
// 检查 mid 对应用户是否存在
|
||||
if (UserHelper.TryGetUser(userCollection, mid, out BindingUser? user))
|
||||
{
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
|
||||
// 检查 stoken 是否存在
|
||||
if (user.HasStoken)
|
||||
{
|
||||
// update stoken
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
user.UpdateSToken(cookie);
|
||||
appDbContext.Users.UpdateAndSave(user.Entity);
|
||||
|
||||
return new(UserOptionResult.Upgraded, mid);
|
||||
}
|
||||
|
||||
await ThreadHelper.SwitchToMainThreadAsync();
|
||||
user.Cookie = cookie;
|
||||
appDbContext.Users.UpdateAndSave(user.Entity);
|
||||
|
||||
return new(UserOptionResult.Updated, mid);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return await TryCreateUserAndAddAsync(cookie).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<ValueResult<UserOptionResult, string>> TryCreateUserAndAddAsync(Cookie cookie)
|
||||
@@ -229,10 +220,8 @@ internal class UserService : IUserService
|
||||
using (IServiceScope scope = scopeFactory.CreateScope())
|
||||
{
|
||||
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
UserClient userClient = scope.ServiceProvider.GetRequiredService<UserClient>();
|
||||
BindingClient bindingClient = scope.ServiceProvider.GetRequiredService<BindingClient>();
|
||||
|
||||
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 @@ internal class UserService : IUserService
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(UserOptionResult.Invalid, null!);
|
||||
return new(UserOptionResult.Invalid, "输入的Cookie无法获取用户信息");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
<TextBlock Text="{Binding UserInfo.Nickname}"/>
|
||||
<TextBlock
|
||||
Text="Stoken"
|
||||
Visibility="{Binding HasSToken,Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Visibility="{Binding HasStoken,Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Foreground="Green"
|
||||
Opacity="0.6"/>
|
||||
@@ -210,7 +210,7 @@
|
||||
<AppBarButton.Flyout>
|
||||
<MenuFlyout>
|
||||
<MenuFlyoutItem Icon="{shcm:FontIcon Glyph=}" Text="网页米游社" Command="{Binding LoginMihoyoBBSCommand}"/>
|
||||
<MenuFlyoutItem Icon="{shcm:FontIcon Glyph=}" Text="米游社" Command="{Binding LoginMihoyoBBS2Command}"/>
|
||||
<!--<MenuFlyoutItem Icon="{shcm:FontIcon Glyph=}" Text="米游社" Command="{Binding LoginMihoyoBBS2Command}"/>-->
|
||||
</MenuFlyout>
|
||||
</AppBarButton.Flyout>
|
||||
</AppBarButton>
|
||||
|
||||
@@ -142,7 +142,7 @@ internal class UserViewModel : ObservableObject
|
||||
private async Task LoginMihoyoBBS2Async()
|
||||
{
|
||||
MainWindow mainWindow = Ioc.Default.GetRequiredService<MainWindow>();
|
||||
await new LoginMihoyoBBSDialog(mainWindow).GetInputAccountPasswordAsync();
|
||||
await new LoginMihoyoBBSDialog(mainWindow).GetInputAccountPasswordAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void LoginMihoyoBBS()
|
||||
|
||||
@@ -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 @@ internal class UserClient
|
||||
{
|
||||
Response<UserFullInfoWrapper>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Cookie)
|
||||
//.SetReferer(ApiEndpoints.BbsReferer)
|
||||
.SetReferer(ApiEndpoints.BbsReferer)
|
||||
.TryCatchGetFromJsonAsync<Response<UserFullInfoWrapper>>(ApiEndpoints.UserFullInfo, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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 partial class Cookie
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存在 LoginTicket
|
||||
/// 此 Cookie 是 SToken
|
||||
/// </summary>
|
||||
/// <returns>是否存在</returns>
|
||||
public bool ContainsLoginTicket()
|
||||
public bool IsStoken()
|
||||
{
|
||||
return inner.ContainsKey(LOGIN_TICKET);
|
||||
}
|
||||
int stokenFlag = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 存在 LToken 与 CookieToken
|
||||
/// </summary>
|
||||
/// <returns>是否存在</returns>
|
||||
public bool ContainsLTokenAndCookieToken()
|
||||
{
|
||||
return inner.ContainsKey(LTOKEN) && inner.ContainsKey(COOKIE_TOKEN);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存在 SToken
|
||||
/// </summary>
|
||||
/// <returns>是否存在</returns>
|
||||
public bool ContainsSToken()
|
||||
{
|
||||
return inner.ContainsKey(STOKEN);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插入 Stoken
|
||||
/// </summary>
|
||||
/// <param name="stuid">stuid</param>
|
||||
/// <param name="cookie">cookie</param>
|
||||
public void InsertSToken(string stuid, Cookie cookie)
|
||||
{
|
||||
inner[STUID] = stuid;
|
||||
inner[STOKEN] = cookie.inner[STOKEN];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除无效的键
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Dictionary2{TKey, TValue}.TryGetValue(TKey, out TValue)"/>
|
||||
public bool TryGetValue(string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
return inner.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetLoginTicket([NotNullWhen(true)] out string? loginTicket)
|
||||
{
|
||||
return inner.TryGetValue(LOGIN_TICKET, out loginTicket);
|
||||
}
|
||||
|
||||
public bool TryGetUid([NotNullWhen(true)] out string? uid)
|
||||
{
|
||||
Dictionary<string, int> uidCounter = new();
|
||||
|
||||
foreach ((string key, string value) in inner)
|
||||
{
|
||||
if (key is ACCOUNT_ID or LOGIN_UID or LTUID or STUID)
|
||||
{
|
||||
uidCounter.Increase(key);
|
||||
stokenFlag++;
|
||||
}
|
||||
}
|
||||
|
||||
if (uidCounter.Count > 0)
|
||||
return stokenFlag == 3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步获取 Mid
|
||||
/// </summary>
|
||||
/// <returns>mid</returns>
|
||||
public async Task<string?> GetMidAsync()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步尝试添加MultiToken
|
||||
/// </summary>
|
||||
/// <param name="uid">uid</param>
|
||||
/// <returns>任务</returns>
|
||||
public async Task TryAddMultiTokenAsync(string uid)
|
||||
{
|
||||
if (TryGetLoginTicket(out string? loginTicket))
|
||||
{
|
||||
// get multitoken
|
||||
Dictionary<string, string> multiToken = await Ioc.Default
|
||||
.GetRequiredService<AuthClient>()
|
||||
.GetMultiTokenByLoginTicketAsync(loginTicket, uid, default)
|
||||
UserInformation? userInfo = await Ioc.Default
|
||||
.GetRequiredService<PassportClient>()
|
||||
.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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据类型输出对应的Cookie
|
||||
/// </summary>
|
||||
/// <param name="type">类型</param>
|
||||
/// <returns>Cookie对应的字符串表示</returns>
|
||||
public string ToString(CookieType type)
|
||||
/// <inheritdoc cref="Dictionary{TKey, TValue}.TryGetValue(TKey, out TValue)"/>
|
||||
public bool TryGetValue(string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
IEnumerable<KeyValuePair<string, string>> results;
|
||||
|
||||
results = type switch
|
||||
{
|
||||
CookieType.None => Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -219,6 +120,6 @@ public partial class Cookie
|
||||
/// <returns>Cookie的字符串表示</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return ToString(CookieType.All);
|
||||
return string.Join(';', inner.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 为MiHoYo接口请求器 <see cref="Requester"/> 提供动态密钥
|
||||
/// </summary>
|
||||
internal abstract class DynamicSecretProvider : Md5Convert
|
||||
{
|
||||
private const string RandomRange = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
/// <summary>
|
||||
/// 创建动态密钥
|
||||
/// </summary>
|
||||
/// <param name="saltType">SALT 类型</param>
|
||||
/// <returns>密钥</returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 为MiHoYo接口请求器 <see cref="Requester"/> 提供2代动态密钥
|
||||
/// </summary>
|
||||
internal abstract class DynamicSecretProvider2 : Md5Convert
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建动态密钥
|
||||
/// </summary>
|
||||
/// <param name="saltType">SALT 类型</param>
|
||||
/// <param name="options">json格式化器</param>
|
||||
/// <param name="queryUrl">查询url</param>
|
||||
/// <param name="postBody">请求体</param>
|
||||
/// <returns>密钥</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 @@ internal static class HttpClientDynamicSecretExtensions
|
||||
client.DefaultRequestHeaders.Set("DS-Option", $"{version}|{salt}|{includeChars}");
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用一代动态密钥执行 GET/POST 操作
|
||||
/// </summary>
|
||||
/// <param name="httpClient">请求器</param>
|
||||
/// <param name="type">SALT 类型</param>
|
||||
/// <returns>响应</returns>
|
||||
[Obsolete]
|
||||
public static HttpClient UsingDynamicSecret1(this HttpClient httpClient, SaltType type)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Set("DS", DynamicSecretProvider.Create(type));
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用二代动态密钥执行 GET/POST 操作
|
||||
/// </summary>
|
||||
/// <param name="httpClient">请求器</param>
|
||||
/// <param name="type">SALT 类型</param>
|
||||
/// <param name="options">选项</param>
|
||||
/// <param name="url">地址</param>
|
||||
/// <returns>响应</returns>
|
||||
[Obsolete]
|
||||
public static IDynamicSecretHttpClient UsingDynamicSecret2(this HttpClient httpClient, SaltType type, JsonSerializerOptions options, string url)
|
||||
{
|
||||
return new DynamicSecretHttpClient(httpClient, type, options, url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用二代动态密钥执行 GET/POST 操作
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">请求数据的类型</typeparam>
|
||||
/// <param name="httpClient">请求器</param>
|
||||
/// <param name="type">SALT 类型</param>
|
||||
/// <param name="options">选项</param>
|
||||
/// <param name="url">地址</param>
|
||||
/// <param name="data">post数据</param>
|
||||
/// <returns>响应</returns>
|
||||
public static IDynamicSecretHttpClient<TValue> UsingDynamicSecret2<TValue>(this HttpClient httpClient, SaltType type, JsonSerializerOptions options, string url, TValue data)
|
||||
where TValue : class
|
||||
{
|
||||
return new DynamicSecretHttpClient<TValue>(httpClient, type, options, url, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置用户的Cookie
|
||||
/// 设置用户的 Cookie
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="user">实体用户</param>
|
||||
@@ -70,7 +68,25 @@ internal static class HttpClientExtensions
|
||||
/// <returns>客户端</returns>
|
||||
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!);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Cookie
|
||||
/// </summary>
|
||||
/// <param name="httpClient">http客户端</param>
|
||||
/// <param name="cookie">Cookie</param>
|
||||
/// <returns>客户端</returns>
|
||||
internal static HttpClient SetRawCookie(this HttpClient httpClient, Cookie cookie)
|
||||
{
|
||||
httpClient.DefaultRequestHeaders.Set("Cookie", cookie.ToString());
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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 @@ internal class PassportClient
|
||||
/// <summary>
|
||||
/// 异步验证Ltoken
|
||||
/// </summary>
|
||||
/// <param name="user">用户</param>
|
||||
/// <param name="cookie">待校验的 Cookie</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>验证信息</returns>
|
||||
[ApiInformation(Cookie = CookieType.Cookie)]
|
||||
public async Task<UserInformation?> VerifyLtokenAsync(User user, CancellationToken token)
|
||||
public async Task<UserInformation?> VerifyLtokenAsync(Cookie cookie, CancellationToken token)
|
||||
{
|
||||
Response<UserInformation>? response = await httpClient
|
||||
.SetUser(user, CookieType.All)
|
||||
.TryCatchPostAsJsonAsync<Timestamp, Response<UserInformation>>(ApiEndpoints.AccountVerifyLtoken, new(), options, logger, token)
|
||||
Response<UserInfoWrapper>? response = await httpClient
|
||||
.SetRawCookie(cookie)
|
||||
.TryCatchPostAsJsonAsync<Timestamp, Response<UserInfoWrapper>>(ApiEndpoints.AccountVerifyLtoken, new(), options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return response?.Data;
|
||||
return response?.Data?.UserInfo;
|
||||
}
|
||||
|
||||
private class Timestamp
|
||||
|
||||
@@ -50,8 +50,8 @@ internal class PassportClient2
|
||||
};
|
||||
|
||||
Response<LoginResult>? resp = await httpClient
|
||||
.UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AccountLoginByPassword, data)
|
||||
.TryCatchPostAsJsonAsync<Response<LoginResult>>(logger, token)
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.PROD, true)
|
||||
.TryCatchPostAsJsonAsync<Dictionary<string, string>, Response<LoginResult>>(ApiEndpoints.AccountLoginByPassword, data, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data;
|
||||
@@ -68,8 +68,8 @@ internal class PassportClient2
|
||||
{
|
||||
Response<UidCookieToken>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
.UsingDynamicSecret2(SaltType.PROD, options, ApiEndpoints.AccountCookieAccountInfoBySToken)
|
||||
.TryCatchGetFromJsonAsync<Response<UidCookieToken>>(logger, token)
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.PROD, true)
|
||||
.TryCatchGetFromJsonAsync<Response<UidCookieToken>>(ApiEndpoints.AccountCookieAccountInfoBySToken, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Web.Hoyolab.Passport;
|
||||
|
||||
/// <summary>
|
||||
/// 用户信息包装器
|
||||
/// </summary>
|
||||
public class UserInfoWrapper
|
||||
{
|
||||
/// <summary>
|
||||
/// 用户信息
|
||||
/// </summary>
|
||||
[JsonPropertyName("user_info")]
|
||||
public UserInformation UserInfo { get; set; } = default!;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
@@ -101,4 +97,4 @@ public class UserInformation
|
||||
/// </summary>
|
||||
[JsonPropertyName("links")]
|
||||
public List<Link> Links { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -41,47 +41,20 @@ internal class AuthClient
|
||||
/// <param name="action">操作</param>
|
||||
/// <param name="user">用户</param>
|
||||
/// <returns>操作凭证</returns>
|
||||
[ApiInformation(Cookie = CookieType.Stoken, Salt = DynamicSecret.SaltType.K2)]
|
||||
[ApiInformation(Cookie = CookieType.Stoken, Salt = SaltType.K2)]
|
||||
public async Task<string?> 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<ActionTicketWrapper>? resp = await httpClient
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true)
|
||||
.TryCatchGetFromJsonAsync<Response<ActionTicketWrapper>>(ApiEndpoints.AuthActionTicket(action, stoken, uid), options, logger)
|
||||
.ConfigureAwait(false);
|
||||
user.Stoken.TryGetValue(Cookie.STOKEN, out string? token);
|
||||
Response<ActionTicketWrapper>? resp = await httpClient
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true)
|
||||
.TryCatchGetFromJsonAsync<Response<ActionTicketWrapper>>(ApiEndpoints.AuthActionTicket(action, token!, user.Aid!), options, logger)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data?.Ticket;
|
||||
}
|
||||
return resp?.Data?.Ticket;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 MultiToken
|
||||
/// </summary>
|
||||
/// <param name="loginTicket">登录票证</param>
|
||||
/// <param name="loginUid">uid</param>
|
||||
/// <param name="token">取消令牌</param>
|
||||
/// <returns>包含token的字典</returns>
|
||||
[Obsolete]
|
||||
public async Task<Dictionary<string, string>> GetMultiTokenByLoginTicketAsync(string loginTicket, string loginUid, CancellationToken token)
|
||||
{
|
||||
Response<ListWrapper<NameToken>>? resp = await httpClient
|
||||
.TryCatchGetFromJsonAsync<Response<ListWrapper<NameToken>>>(ApiEndpoints.AuthMultiToken(loginTicket, loginUid), options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (resp?.Data != null)
|
||||
{
|
||||
Dictionary<string, string> 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();
|
||||
}
|
||||
}
|
||||
@@ -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 @@ internal class BindingClient2
|
||||
public async Task<GameAuthKey?> GenerateAuthenticationKeyAsync(User user, GenAuthKeyData data, CancellationToken token = default)
|
||||
{
|
||||
Response<GameAuthKey>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
.SetReferer("https://app.mihoyo.com")
|
||||
.UsingDynamicSecret1(SaltType.K2)
|
||||
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.BindingGenAuthKey, data, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
.SetUser(user, CookieType.Stoken)
|
||||
.SetReferer("https://app.mihoyo.com")
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen1, SaltType.K2, true)
|
||||
.TryCatchPostAsJsonAsync<GenAuthKeyData, Response<GameAuthKey>>(ApiEndpoints.BindingGenAuthKey, data, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return resp?.Data;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using Snap.Hutao.Extension;
|
||||
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 @@ internal class GameRecordClient
|
||||
|
||||
Response<CharacterWrapper>? resp = await httpClient
|
||||
.SetUser(user, CookieType.Cookie)
|
||||
.UsingDynamicSecret2(SaltType.X4, options, ApiEndpoints.GameRecordCharacter, data)
|
||||
.TryCatchPostAsJsonAsync<Response<CharacterWrapper>>(logger, token)
|
||||
.UseDynamicSecret(DynamicSecretVersion.Gen2, SaltType.X4, false)
|
||||
.TryCatchPostAsJsonAsync<CharacterData, Response<CharacterWrapper>>(ApiEndpoints.GameRecordCharacter, data, options, logger, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return EnumerableExtension.EmptyIfNull(resp?.Data?.Avatars);
|
||||
|
||||
@@ -177,13 +177,8 @@ internal class HomaClient
|
||||
{
|
||||
Must.NotNull(user.SelectedUserGameRole!);
|
||||
|
||||
PlayerInfo? playerInfo = await gameRecordClient
|
||||
.GetPlayerInfoAsync(user.Entity, user.SelectedUserGameRole, token)
|
||||
.ConfigureAwait(false);
|
||||
Must.NotNull(playerInfo!);
|
||||
|
||||
List<Character> characters = await gameRecordClient
|
||||
.GetCharactersAsync(user.Entity, user.SelectedUserGameRole, playerInfo, token)
|
||||
.GetCharactersAsync(user.Entity, user.SelectedUserGameRole, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
SpiralAbyss? spiralAbyssInfo = await gameRecordClient
|
||||
|
||||
Reference in New Issue
Block a user