From 247c401a5bb4accd8d54be8e09af83bc1185069d Mon Sep 17 00:00:00 2001 From: HolographicHat Date: Sat, 30 Nov 2024 13:52:39 +0800 Subject: [PATCH] aot --- YaeAchievement.csproj | 6 +- src/Export.cs | 103 +++++++++--------------- src/GlobalVars.cs | 9 ++- src/NativeMethods.json | 2 +- src/Outputs/.gitkeep | 0 src/Outputs/Paimon.cs | 37 +++++++++ src/Outputs/Seelie.cs | 39 +++++++++ src/Outputs/UIAF.cs | 65 +++++++++++++++ src/Parsers/AchievementAllDataNotify.cs | 15 ++++ src/Utils.cs | 1 - 10 files changed, 205 insertions(+), 72 deletions(-) delete mode 100644 src/Outputs/.gitkeep create mode 100644 src/Outputs/Paimon.cs create mode 100644 src/Outputs/Seelie.cs create mode 100644 src/Outputs/UIAF.cs diff --git a/YaeAchievement.csproj b/YaeAchievement.csproj index 3354ff4..dfa2f27 100644 --- a/YaeAchievement.csproj +++ b/YaeAchievement.csproj @@ -21,11 +21,11 @@ - - + + all - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Export.cs b/src/Export.cs index 3f69fcc..465f853 100644 --- a/src/Export.cs +++ b/src/Export.cs @@ -5,6 +5,7 @@ using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.Win32; +using YaeAchievement.Outputs; using YaeAchievement.Parsers; using YaeAchievement.res; @@ -14,10 +15,6 @@ public static class Export { public static uint ExportTo { get; set; } = uint.MaxValue; - private static readonly JsonSerializerOptions JsonOpts = new () { - WriteIndented = true - }; - public static void Choose(AchievementAllDataNotify data) { if (ExportTo == uint.MaxValue) { Console.Write(App.ExportChoose); @@ -41,12 +38,8 @@ public static class Export { })).Invoke(data); } - private class CocogoatResponse { - [JsonPropertyName("key")] public string Code { get; init; } = null!; - } - private static void ToCocogoat(AchievementAllDataNotify data) { - var result = JsonSerializer.Serialize(ExportToUIAFApp(data)); + var result = UIAFSerializer.Serialize(data); using var request = new HttpRequestMessage(); request.Method = HttpMethod.Post; request.RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}"); @@ -57,18 +50,15 @@ public static class Export { return; } var responseText = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - var responseJson = JsonSerializer.Deserialize(responseText)!; - Console.WriteLine(Utils.ShellOpen($"https://cocogoat.work/achievement?memo={responseJson.Code}") + var responseJson = JsonSerializer.Deserialize(responseText, CocogoatResponseContext.Default.CocogoatResponse)!; + Console.WriteLine(Utils.ShellOpen($"https://cocogoat.work/achievement?memo={responseJson.Key}") ? App.ExportToCocogoatSuccess - : $"https://cocogoat.work/achievement?memo={responseJson.Code}"); + : $"https://cocogoat.work/achievement?memo={responseJson.Key}"); } private static void ToWxApp1(AchievementAllDataNotify data) { var id = Guid.NewGuid().ToString("N").Substring(20, 8); - var result = JsonSerializer.Serialize(new Dictionary { - { "key", id }, - { "data", ExportToUIAFApp(data) } - }); + var result = WxApp1Serializer.Serialize(data, id); using var request = new HttpRequestMessage(); request.Method = HttpMethod.Post; request.RequestUri = new Uri("https://api.qyinter.com/achievementRedis"); @@ -79,7 +69,7 @@ public static class Export { private static void ToHuTao(AchievementAllDataNotify data) { if (CheckWinUIAppScheme("hutao")) { - Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data))); + Utils.CopyToClipboard(UIAFSerializer.Serialize(data)); Utils.ShellOpen("hutao://achievement/import"); Console.WriteLine(App.ExportToSnapGenshinSuccess); } else { @@ -90,7 +80,7 @@ public static class Export { private static void ToXunkong(AchievementAllDataNotify data) { if (CheckWinUIAppScheme("xunkong")) { - Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data))); + Utils.CopyToClipboard(UIAFSerializer.Serialize(data)); Utils.ShellOpen("xunkong://import-achievement?caller=YaeAchievement&from=clipboard"); Console.WriteLine(App.ExportToXunkongSuccess); } else { @@ -101,7 +91,7 @@ public static class Export { private static void ToTeyvatGuide(AchievementAllDataNotify data) { if (Process.GetProcessesByName("TeyvatGuide").Length != 0) { - Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data))); + Utils.CopyToClipboard(UIAFSerializer.Serialize(data)); Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement"); Console.WriteLine(App.ExportToTauriSuccess); } else { @@ -113,39 +103,21 @@ public static class Export { // ReSharper disable once InconsistentNaming private static void ToUIAFJson(AchievementAllDataNotify data) { var path = Path.GetFullPath($"uiaf-{DateTime.Now:yyyyMMddHHmmss}.json"); - if (TryWriteToFile(path, JsonSerializer.Serialize(ExportToUIAFApp(data)))) { + if (TryWriteToFile(path, UIAFSerializer.Serialize(data))) { Console.WriteLine(App.ExportToFileSuccess, path); } } private static void ToPaimon(AchievementAllDataNotify data) { - var info = GlobalVars.AchievementInfo.Items.ToDictionary(pair => pair.Key, pair => pair.Value.Group); - var final = new Dictionary>> { - ["achievement"] = data.AchievementList - .Where(achievement => achievement.Status is AchievementStatus.Finished or AchievementStatus.RewardTaken) - .Where(achievement => info.ContainsKey(achievement.Id)) - .GroupBy(achievement => info[achievement.Id], achievement => achievement.Id) - .OrderBy(group => group.Key) - .ToDictionary(group => group.Key, group => group.ToDictionary(id => id, _ => true)) - }; var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-paimon.json"); - if (TryWriteToFile(path, JsonSerializer.Serialize(final))) { + if (TryWriteToFile(path, PaimonSerializer.Serialize(data))) { Console.WriteLine(App.ExportToFileSuccess, path); } } private static void ToSeelie(AchievementAllDataNotify data) { - var output = new Dictionary>(); - foreach (var ach in data.AchievementList.Where(a => a.Status is AchievementStatus.Finished or AchievementStatus.RewardTaken)) { - output[ach.Id] = new Dictionary { - ["done"] = true - }; - } - var final = new Dictionary>> { - ["achievements"] = output.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value) - }; var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-seelie.json"); - if (TryWriteToFile(path, JsonSerializer.Serialize(final))) { + if (TryWriteToFile(path, SeelieSerializer.Serialize(data))) { Console.WriteLine(App.ExportToFileSuccess, path); } } @@ -187,34 +159,12 @@ public static class Export { private static void ToRawJson(AchievementAllDataNotify data) { var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json"); - var text = JsonSerializer.Serialize(data, JsonOpts); + var text = AchievementRawDataSerializer.Serialize(data); if (TryWriteToFile(path, text)) { Console.WriteLine(App.ExportToFileSuccess, path); } } - // ReSharper disable once InconsistentNaming - private static Dictionary ExportToUIAFApp(AchievementAllDataNotify data) { - var output = data.AchievementList - .Where(a => (uint)a.Status > 1 || a.CurrentProgress > 0) - .Select(ach => new Dictionary { - ["id"] = ach.Id, - ["status"] = (uint) ach.Status, - ["current"] = ach.CurrentProgress, - ["timestamp"] = ach.FinishTimestamp - }) - .ToList(); - return new Dictionary { - ["info"] = new Dictionary { - ["export_app"] = "YaeAchievement", - ["export_timestamp"] = DateTimeOffset.Now.ToUnixTimeSeconds(), - ["export_app_version"] = GlobalVars.AppVersionName, - ["uiaf_version"] = "v1.1" - }, - ["list"] = output - }; - } - // ReSharper disable once InconsistentNaming private static bool CheckWinUIAppScheme(string protocol) { return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}"; @@ -252,3 +202,30 @@ public static class Export { } } } + +public class WxApp1Root { + + public string Key { get; init; } = null!; + + public UIAFRoot Data { get; init; } = null!; + +} + +[JsonSerializable(typeof(WxApp1Root))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Serialization, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower +)] +public partial class WxApp1Serializer : JsonSerializerContext { + + public static string Serialize(AchievementAllDataNotify ntf, string key) => JsonSerializer.Serialize(new WxApp1Root { + Key = key, + Data = Outputs.UIAFRoot.FromNotify(ntf) + }, Default.WxApp1Root); +} + +public record CocogoatResponse(string Key); + +[JsonSerializable(typeof(CocogoatResponse))] +[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)] +public partial class CocogoatResponseContext : JsonSerializerContext; diff --git a/src/GlobalVars.cs b/src/GlobalVars.cs index 288004d..78efb45 100644 --- a/src/GlobalVars.cs +++ b/src/GlobalVars.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; using Proto; namespace YaeAchievement; @@ -10,7 +11,6 @@ namespace YaeAchievement; public static class GlobalVars { - public static bool DebugProxy => false; public static bool UnexpectedExit { get; set; } = true; public static bool PauseOnExit { get; set; } = true; public static Version AppVersion { get; } = Assembly.GetEntryAssembly()!.GetName().Version!; @@ -28,11 +28,12 @@ public static class GlobalVars { public const string RinBucketHost = "https://rin.holohat.work"; public const string SakuraBucketHost = "https://cn-cd-1259389942.file.myqcloud.com"; - public static AchievementInfo AchievementInfo { get; } + [field:MaybeNull] + public static AchievementInfo AchievementInfo => + field ??= AchievementInfo.Parser.ParseFrom(Utils.GetBucketFile("schicksal/metadata").GetAwaiter().GetResult()); static GlobalVars() { Directory.CreateDirectory(DataPath); Directory.CreateDirectory(CachePath); - AchievementInfo = AchievementInfo.Parser.ParseFrom(Utils.GetBucketFile("schicksal/metadata").GetAwaiter().GetResult()); } } diff --git a/src/NativeMethods.json b/src/NativeMethods.json index 2fc0fd4..f10977f 100644 --- a/src/NativeMethods.json +++ b/src/NativeMethods.json @@ -2,5 +2,5 @@ "$schema": "https://aka.ms/CsWin32.schema.json", "className": "Native", "allowMarshaling": false, - "public": true + "public": true } \ No newline at end of file diff --git a/src/Outputs/.gitkeep b/src/Outputs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/Outputs/Paimon.cs b/src/Outputs/Paimon.cs new file mode 100644 index 0000000..5dc7680 --- /dev/null +++ b/src/Outputs/Paimon.cs @@ -0,0 +1,37 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using YaeAchievement.Parsers; + +namespace YaeAchievement.Outputs; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable PropertyCanBeMadeInitOnly.Global +#pragma warning disable CA1822 // ReSharper disable MemberCanBeMadeStatic.Global + +public class PaimonRoot { + + public Dictionary> Achievement { get; set; } = null!; + + public static PaimonRoot FromNotify(AchievementAllDataNotify ntf) { + var info = GlobalVars.AchievementInfo.Items.ToDictionary(pair => pair.Key, pair => pair.Value.Group); + return new PaimonRoot { + Achievement = ntf.AchievementList + .Where(a => a.Status >= AchievementStatus.Finished && info.ContainsKey(a.Id)) + .GroupBy(a => info[a.Id], a => a.Id) + .OrderBy(g => g.Key) + .ToDictionary(g => g.Key, g => g.ToDictionary(id => id, _ => true)) + }; + } +} + +[JsonSerializable(typeof(PaimonRoot))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Serialization, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower +)] +public partial class PaimonSerializer : JsonSerializerContext { + + public static string Serialize(AchievementAllDataNotify ntf) { + return JsonSerializer.Serialize(Outputs.PaimonRoot.FromNotify(ntf), Default.PaimonRoot); + } +} diff --git a/src/Outputs/Seelie.cs b/src/Outputs/Seelie.cs new file mode 100644 index 0000000..1e847d5 --- /dev/null +++ b/src/Outputs/Seelie.cs @@ -0,0 +1,39 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using YaeAchievement.Parsers; + +namespace YaeAchievement.Outputs; + +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable PropertyCanBeMadeInitOnly.Global +#pragma warning disable CA1822 // ReSharper disable MemberCanBeMadeStatic.Global + +public class SeelieRoot { + + public class AchievementFinishStatus { + + public bool Done => true; + + } + + public Dictionary Achievements { get; set; } = null!; + + public static SeelieRoot FromNotify(AchievementAllDataNotify ntf) => new () { + Achievements = ntf.AchievementList + .Where(a => a.Status >= AchievementStatus.Finished) + .OrderBy(a => a.Id) + .ToDictionary(a => a.Id, _ => new AchievementFinishStatus()) + }; +} + +[JsonSerializable(typeof(SeelieRoot))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Serialization, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower +)] +public partial class SeelieSerializer : JsonSerializerContext { + + public static string Serialize(AchievementAllDataNotify ntf) { + return JsonSerializer.Serialize(Outputs.SeelieRoot.FromNotify(ntf), Default.SeelieRoot); + } +} diff --git a/src/Outputs/UIAF.cs b/src/Outputs/UIAF.cs new file mode 100644 index 0000000..8078c31 --- /dev/null +++ b/src/Outputs/UIAF.cs @@ -0,0 +1,65 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using YaeAchievement.Parsers; + +namespace YaeAchievement.Outputs; + +// ReSharper disable InconsistentNaming +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable PropertyCanBeMadeInitOnly.Global +#pragma warning disable CA1822 // ReSharper disable MemberCanBeMadeStatic.Global + +public class UApplicationInfo { + + public string ExportApp => "YaeAchievement"; + + public string ExportAppVersion => GlobalVars.AppVersionName; + + public long ExportTimestamp => DateTimeOffset.Now.ToUnixTimeSeconds(); + + public string UIAFVersion => "v1.1"; + +} + +public class UAchievementInfo { + + public uint Id { get; set; } + + public uint Status { get; set; } + + public uint Current { get; set; } + + public uint Timestamp { get; set; } + +} + +public class UIAFRoot { + + public UApplicationInfo Info => new (); + + public IEnumerable List { get; set; } = null!; + + public static UIAFRoot FromNotify(AchievementAllDataNotify ntf) => new () { + List = ntf.AchievementList + .Where(a => a.Status >= AchievementStatus.Finished || a.CurrentProgress > 0) + .Select(a => new UAchievementInfo { + Id = a.Id, + Status = (uint) a.Status, + Current = a.CurrentProgress, + Timestamp = a.FinishTimestamp + }) + }; + +} + +[JsonSerializable(typeof(UIAFRoot))] +[JsonSourceGenerationOptions( + GenerationMode = JsonSourceGenerationMode.Serialization, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower +)] +public partial class UIAFSerializer : JsonSerializerContext { + + public static string Serialize(AchievementAllDataNotify ntf) { + return JsonSerializer.Serialize(Outputs.UIAFRoot.FromNotify(ntf), Default.UIAFRoot); + } +} diff --git a/src/Parsers/AchievementAllDataNotify.cs b/src/Parsers/AchievementAllDataNotify.cs index da29e62..236f42c 100644 --- a/src/Parsers/AchievementAllDataNotify.cs +++ b/src/Parsers/AchievementAllDataNotify.cs @@ -1,4 +1,6 @@ using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization; using Google.Protobuf; using YaeAchievement.res; @@ -109,3 +111,16 @@ public class AchievementAllDataNotify { private static extern byte[] ReadRawBytes(CodedInputStream stream, int size); } + +[JsonSerializable(typeof(AchievementAllDataNotify))] +[JsonSourceGenerationOptions( + WriteIndented = true, + GenerationMode = JsonSourceGenerationMode.Serialization, + PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower +)] +public partial class AchievementRawDataSerializer : JsonSerializerContext { + + public static string Serialize(AchievementAllDataNotify ntf) { + return JsonSerializer.Serialize(ntf, Default.AchievementAllDataNotify); + } +} diff --git a/src/Utils.cs b/src/Utils.cs index 35c6c13..df9a014 100644 --- a/src/Utils.cs +++ b/src/Utils.cs @@ -18,7 +18,6 @@ namespace YaeAchievement; public static class Utils { public static readonly HttpClient CHttpClient = new (new HttpClientHandler { - Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null, AutomaticDecompression = DecompressionMethods.Brotli | DecompressionMethods.GZip }) { DefaultRequestHeaders = {