mirror of
https://github.com/HolographicHat/Yae.git
synced 2026-05-01 10:09:34 +08:00
2
.github/workflows/dotnet.yml
vendored
2
.github/workflows/dotnet.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: 7.0.x
|
dotnet-version: 8.0.x
|
||||||
- name: Restore dependencies
|
- name: Restore dependencies
|
||||||
run: dotnet restore
|
run: dotnet restore
|
||||||
- name: Build
|
- name: Build
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0-windows</TargetFramework>
|
<TargetFramework>net8.0-windows</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<LangVersion>preview</LangVersion>
|
<LangVersion>preview</LangVersion>
|
||||||
@@ -21,11 +21,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.22.1" />
|
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
|
||||||
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.2.188-beta">
|
<PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.49-beta">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.53.0">
|
<PackageReference Include="Grpc.Tools" Version="2.62.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
using System.Net;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using YaeAchievement.AppCenterSDK.Models;
|
|
||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK;
|
|
||||||
|
|
||||||
public static class AppCenter {
|
|
||||||
|
|
||||||
private const string AppSecret = "648b83bf-d439-49bd-97f4-e1e506bdfe39";
|
|
||||||
private const string ApiUrl = "https://in.appcenter.ms/logs?api-version=1.0.0";
|
|
||||||
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
public static Guid? SessionID { get; private set; }
|
|
||||||
public static readonly string DeviceID;
|
|
||||||
public static readonly Device DeviceInfo;
|
|
||||||
private static List<Log> Queue;
|
|
||||||
private static readonly Lazy<HttpClient> httpClient = new(() => new HttpClient(new HttpClientHandler {
|
|
||||||
Proxy = GlobalVars.DebugProxy ? new WebProxy("http://127.0.0.1:8888") : null
|
|
||||||
}) {
|
|
||||||
DefaultRequestHeaders = {
|
|
||||||
{ "Install-ID", DeviceID },
|
|
||||||
{ "App-Secret", AppSecret }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
static AppCenter() {
|
|
||||||
Queue = new List<Log>();
|
|
||||||
DeviceID = DeviceHelper.GetDeviceID();
|
|
||||||
DeviceInfo = new Device();
|
|
||||||
var running = true;
|
|
||||||
Task.Run(() => {
|
|
||||||
// ReSharper disable once LoopVariableIsNeverChangedInsideLoop
|
|
||||||
while (running) {
|
|
||||||
Upload();
|
|
||||||
Thread.Sleep(5 * 1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
AppDomain.CurrentDomain.ProcessExit += (_, _) => {
|
|
||||||
running = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper restore InconsistentNaming
|
|
||||||
public static void Upload() {
|
|
||||||
if (Queue.Count == 0) return;
|
|
||||||
var uploadStatus = "";
|
|
||||||
do {
|
|
||||||
Queue = Queue.Select(log => {
|
|
||||||
log.Status = LogStatus.Uploading;
|
|
||||||
return log;
|
|
||||||
}).ToList();
|
|
||||||
using var uploadContent = new StringContent(Queue.ToJson());
|
|
||||||
try {
|
|
||||||
using var response = httpClient.Value.PostAsync(ApiUrl, uploadContent).Result;
|
|
||||||
var result = response.Content.ReadFromJsonAsync<LogUploadResult>().GetAwaiter().GetResult();
|
|
||||||
uploadStatus = result!.Status;
|
|
||||||
} catch (Exception) {
|
|
||||||
// ignored
|
|
||||||
}
|
|
||||||
} while (uploadStatus != "Success");
|
|
||||||
Queue.RemoveAll(log => log.Status == LogStatus.Uploading);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Init() {
|
|
||||||
new StartServiceLog("Analytics", "Crashes").Enqueue();
|
|
||||||
SessionID = Guid.NewGuid();
|
|
||||||
new StartSessionLog().Enqueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void TrackCrash(Exception exception, bool fatal = true) {
|
|
||||||
new ManagedErrorLog(exception, fatal).Enqueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Enqueue(this Log log) {
|
|
||||||
Queue.Add(log);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ToJson(this IEnumerable<Log> queue) {
|
|
||||||
return LogSerializer.Serialize(new LogContainer(queue));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
using Microsoft.Win32;
|
|
||||||
using Windows.Win32;
|
|
||||||
using Windows.Win32.Foundation;
|
|
||||||
using Windows.Win32.Graphics.Gdi;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK;
|
|
||||||
|
|
||||||
#pragma warning disable CA1416
|
|
||||||
public static class DeviceHelper {
|
|
||||||
|
|
||||||
public static string? GetOem() {
|
|
||||||
using var root = Registry.LocalMachine;
|
|
||||||
using var sub = root.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS");
|
|
||||||
var oem = sub?.GetValue("SystemManufacturer") as string;
|
|
||||||
return oem == "System manufacturer" ? null : oem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string? GetModel() {
|
|
||||||
using var root = Registry.LocalMachine;
|
|
||||||
using var sub = root.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\BIOS");
|
|
||||||
var model = sub?.GetValue("SystemProductName") as string;
|
|
||||||
return model == "System Product Name" ? null : model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetScreenSize() {
|
|
||||||
var desktop = Native.GetDC(HWND.Null);
|
|
||||||
var size = $"{Native.GetDeviceCaps(desktop, GET_DEVICE_CAPS_INDEX.DESKTOPHORZRES)}x{Native.GetDeviceCaps(desktop, GET_DEVICE_CAPS_INDEX.DESKTOPVERTRES)}";
|
|
||||||
_ = Native.ReleaseDC(HWND.Null, desktop);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string? GetCountry() {
|
|
||||||
using var root = Registry.CurrentUser;
|
|
||||||
using var sub = root.OpenSubKey("Control Panel\\International\\Geo");
|
|
||||||
return sub?.GetValue("Name") as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetSystemVersion() {
|
|
||||||
using var root = Registry.LocalMachine;
|
|
||||||
using var sub = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
|
|
||||||
var majorVersion = sub?.GetValue("CurrentMajorVersionNumber");
|
|
||||||
if (majorVersion != null) {
|
|
||||||
var minorVersion = sub?.GetValue("CurrentMinorVersionNumber", "0");
|
|
||||||
var buildNumber = sub?.GetValue("CurrentBuildNumber", "0");
|
|
||||||
return $"{majorVersion}.{minorVersion}.{buildNumber}";
|
|
||||||
} else {
|
|
||||||
var version = sub?.GetValue("CurrentVersion", "0.0");
|
|
||||||
var buildNumber = sub?.GetValue("CurrentBuild", "0");
|
|
||||||
return $"{version}.{buildNumber}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetSystemBuild() {
|
|
||||||
using var root = Registry.LocalMachine;
|
|
||||||
using var sub = root.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
|
|
||||||
return (int) (sub?.GetValue("UBR") ?? 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReSharper disable once InconsistentNaming
|
|
||||||
public static string GetDeviceID() {
|
|
||||||
using var sdk = Registry.CurrentUser.OpenSubKey("SOFTWARE\\miHoYoSDK");
|
|
||||||
if (sdk?.GetValue("MIHOYOSDK_DEVICE_ID") is not string id) {
|
|
||||||
id = $"{Random.Shared.NextInt64().ToString().SHA1Hash().ToLower()}{DateTimeOffset.Now.ToUnixTimeMilliseconds()}";
|
|
||||||
sdk?.SetValue("MIHOYOSDK_DEVICE_ID", id);
|
|
||||||
}
|
|
||||||
id = id.MD5Hash().ToLower();
|
|
||||||
return $"{id[..8]}-{id[8..12]}-{id[12..16]}-{id[16..20]}-{id[20..]}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#pragma warning restore CA1416
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK;
|
|
||||||
|
|
||||||
public static class ErrorLogHelper {
|
|
||||||
public static MException CreateModelExceptionAndBinaries(Exception exception) {
|
|
||||||
var modelException = new MException {
|
|
||||||
Type = exception.GetType().ToString(),
|
|
||||||
Message = exception.Message,
|
|
||||||
StackTrace = exception.StackTrace
|
|
||||||
};
|
|
||||||
if (exception is AggregateException aggregateException) {
|
|
||||||
if (aggregateException.InnerExceptions.Count != 0) {
|
|
||||||
modelException.InnerExceptions = new List<MException>();
|
|
||||||
foreach (var innerException in aggregateException.InnerExceptions) {
|
|
||||||
modelException.InnerExceptions.Add(CreateModelExceptionAndBinaries(innerException));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (exception.InnerException != null) {
|
|
||||||
modelException.InnerExceptions ??= new List<MException>();
|
|
||||||
modelException.InnerExceptions.Add(CreateModelExceptionAndBinaries(exception.InnerException));
|
|
||||||
}
|
|
||||||
return modelException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public class Device {
|
|
||||||
|
|
||||||
public string SdkName { get; set; } = "appcenter.wpf.netcore";
|
|
||||||
|
|
||||||
public string SdkVersion { get; set; } = "4.5.0";
|
|
||||||
|
|
||||||
public string OsName { get; set; } = "WINDOWS";
|
|
||||||
|
|
||||||
public string OsVersion { get; set; } = DeviceHelper.GetSystemVersion();
|
|
||||||
|
|
||||||
public string OsBuild { get; set; } = $"{DeviceHelper.GetSystemVersion()}.{DeviceHelper.GetSystemBuild()}";
|
|
||||||
|
|
||||||
public string? Model { get; set; } = DeviceHelper.GetModel();
|
|
||||||
|
|
||||||
public string? OemName { get; set; } = DeviceHelper.GetOem();
|
|
||||||
|
|
||||||
public string ScreenSize { get; set; } = DeviceHelper.GetScreenSize();
|
|
||||||
|
|
||||||
public string CarrierCountry { get; set; } = DeviceHelper.GetCountry() ?? "CN";
|
|
||||||
|
|
||||||
public string Locale { get; set; } = CultureInfo.CurrentCulture.Name;
|
|
||||||
|
|
||||||
public int TimeZoneOffset { get; set; } = (int) TimeZoneInfo.Local.BaseUtcOffset.TotalMinutes;
|
|
||||||
|
|
||||||
public string AppVersion { get; set; } = GlobalVars.AppVersionName;
|
|
||||||
|
|
||||||
public string AppBuild { get; set; } = GlobalVars.AppVersionCode.ToString();
|
|
||||||
|
|
||||||
public string AppNamespace { get; set; } = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType?.Namespace ?? string.Empty;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
[LogId(JsonIdentifier)]
|
|
||||||
public class EventLog(string name) : LogWithProperties {
|
|
||||||
|
|
||||||
public const string JsonIdentifier = "event";
|
|
||||||
|
|
||||||
public Guid Id { get; set; } = Guid.NewGuid();
|
|
||||||
|
|
||||||
public string Name { get; set; } = name;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
[LogId(JsonIdentifier)]
|
|
||||||
public class HandledErrorLog : LogWithProperties {
|
|
||||||
|
|
||||||
public const string JsonIdentifier = "handledError";
|
|
||||||
|
|
||||||
public HandledErrorLog(MException exception) {
|
|
||||||
Id = Guid.NewGuid();
|
|
||||||
Exception = exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid? Id { get; set; }
|
|
||||||
|
|
||||||
public MException Exception { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public abstract class Log {
|
|
||||||
|
|
||||||
[JsonPropertyName("sid")]
|
|
||||||
public Guid? Session { get; set; } = AppCenter.SessionID;
|
|
||||||
|
|
||||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
|
||||||
|
|
||||||
public Device Device { get; set; } = AppCenter.DeviceInfo;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
internal LogStatus Status = LogStatus.Pending;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum LogStatus {
|
|
||||||
Pending, Uploading, Uploaded
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public class LogContainer {
|
|
||||||
|
|
||||||
public LogContainer(IEnumerable<Log> logs) {
|
|
||||||
Logs = logs;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Log> Logs { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public class LogUploadResult {
|
|
||||||
|
|
||||||
public string Status { get; set; } = null!;
|
|
||||||
|
|
||||||
public Guid[] ValidDiagnosticsIds { get; set; } = Array.Empty<Guid>();
|
|
||||||
|
|
||||||
public Guid[] ThrottledDiagnosticsIds { get; set; } = Array.Empty<Guid>();
|
|
||||||
|
|
||||||
public Guid CorrelationId { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public class LogWithProperties : Log {
|
|
||||||
|
|
||||||
public IDictionary<string, string> Properties { get; set; } = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public class MException {
|
|
||||||
|
|
||||||
public string Type { get; set; } = "UnknownType";
|
|
||||||
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
public string? StackTrace { get; set; }
|
|
||||||
|
|
||||||
public IList<StackFrame>? Frames { get; set; }
|
|
||||||
|
|
||||||
public IList<MException>? InnerExceptions { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using System.Diagnostics;
|
|
||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
[LogId(JsonIdentifier)]
|
|
||||||
public class ManagedErrorLog : Log {
|
|
||||||
|
|
||||||
public const string JsonIdentifier = "managedError";
|
|
||||||
|
|
||||||
public ManagedErrorLog(
|
|
||||||
Exception exception,
|
|
||||||
bool fatal = true
|
|
||||||
) {
|
|
||||||
var p = Process.GetCurrentProcess();
|
|
||||||
Id = Guid.NewGuid();
|
|
||||||
Fatal = fatal;
|
|
||||||
UserId = AppCenter.DeviceID;
|
|
||||||
ProcessId = p.Id;
|
|
||||||
Exception = ErrorLogHelper.CreateModelExceptionAndBinaries(exception);
|
|
||||||
ProcessName = p.ProcessName;
|
|
||||||
Architecture = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
|
|
||||||
AppLaunchTimestamp = p.StartTime.ToUniversalTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Guid Id { get; set; }
|
|
||||||
|
|
||||||
public string? UserId { get; set; }
|
|
||||||
|
|
||||||
public int ProcessId { get; set; }
|
|
||||||
|
|
||||||
public string ProcessName { get; set; }
|
|
||||||
|
|
||||||
public bool Fatal { get; set; }
|
|
||||||
|
|
||||||
public DateTime? AppLaunchTimestamp { get; set; }
|
|
||||||
|
|
||||||
public string? Architecture { get; set; }
|
|
||||||
|
|
||||||
public MException Exception { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
[LogId(JsonIdentifier)]
|
|
||||||
public class PageLog : LogWithProperties {
|
|
||||||
|
|
||||||
public const string JsonIdentifier = "page";
|
|
||||||
|
|
||||||
public PageLog(string name) {
|
|
||||||
Name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public sealed class LogIdAttribute(string id) : Attribute {
|
|
||||||
|
|
||||||
public string Id { get; } = id;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
public class GuidConverter : JsonConverter<Guid> {
|
|
||||||
|
|
||||||
public static GuidConverter Instance { get; } = new ();
|
|
||||||
|
|
||||||
public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) {
|
|
||||||
writer.WriteStringValue(value.ToString("D"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LogJsonConverter : JsonConverter<Log> {
|
|
||||||
|
|
||||||
public static LogJsonConverter Instance { get; } = new ();
|
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions SerializationSettings;
|
|
||||||
|
|
||||||
static LogJsonConverter() {
|
|
||||||
SerializationSettings = new JsonSerializerOptions {
|
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
ReferenceHandler = ReferenceHandler.IgnoreCycles,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType) => typeof(Log).IsAssignableFrom(objectType);
|
|
||||||
|
|
||||||
public override Log Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, Log value, JsonSerializerOptions options) {
|
|
||||||
var attr = value.GetType().GetCustomAttribute<LogIdAttribute>();
|
|
||||||
if (attr == null) {
|
|
||||||
throw new JsonException("Log type is missing LogTypeAttribute");
|
|
||||||
}
|
|
||||||
var cNode = JsonSerializer.SerializeToNode((object) value, SerializationSettings)!;
|
|
||||||
cNode["type"] = attr.Id;
|
|
||||||
writer.WriteRawValue(cNode.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
public static class LogSerializer {
|
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions SerializationSettings;
|
|
||||||
|
|
||||||
static LogSerializer() {
|
|
||||||
SerializationSettings = new JsonSerializerOptions {
|
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
ReferenceHandler = ReferenceHandler.IgnoreCycles,
|
|
||||||
Converters = {
|
|
||||||
GuidConverter.Instance,
|
|
||||||
LogJsonConverter.Instance,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Serialize(LogContainer logContainer) {
|
|
||||||
return JsonSerializer.Serialize(logContainer, SerializationSettings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
public class StackFrame {
|
|
||||||
|
|
||||||
public StackFrame(string address, string code, string className, string methodName, int? lineNumber, string fileName) {
|
|
||||||
Address = address;
|
|
||||||
Code = code;
|
|
||||||
ClassName = className;
|
|
||||||
MethodName = methodName;
|
|
||||||
LineNumber = lineNumber;
|
|
||||||
FileName = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Address { get; set; }
|
|
||||||
|
|
||||||
public string Code { get; set; }
|
|
||||||
|
|
||||||
public string ClassName { get; set; }
|
|
||||||
|
|
||||||
public string MethodName { get; set; }
|
|
||||||
|
|
||||||
public int? LineNumber { get; set; }
|
|
||||||
|
|
||||||
public string FileName { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
[LogId(JsonIdentifier)]
|
|
||||||
public class StartServiceLog : Log {
|
|
||||||
|
|
||||||
public const string JsonIdentifier = "startService";
|
|
||||||
|
|
||||||
public StartServiceLog(params string[] services) {
|
|
||||||
Services = services;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string[] Services { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using YaeAchievement.AppCenterSDK.Models.Serialization;
|
|
||||||
|
|
||||||
namespace YaeAchievement.AppCenterSDK.Models;
|
|
||||||
|
|
||||||
[LogId(JsonIdentifier)]
|
|
||||||
public class StartSessionLog : Log {
|
|
||||||
public const string JsonIdentifier = "startSession";
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ using YaeAchievement.res;
|
|||||||
|
|
||||||
namespace YaeAchievement;
|
namespace YaeAchievement;
|
||||||
|
|
||||||
public static class AppConfig {
|
public static partial class AppConfig {
|
||||||
|
|
||||||
public static string GamePath { get; private set; } = null!;
|
public static string GamePath { get; private set; } = null!;
|
||||||
|
|
||||||
@@ -52,11 +52,15 @@ public static class AppConfig {
|
|||||||
try {
|
try {
|
||||||
File.Delete(copiedLogFilePath);
|
File.Delete(copiedLogFilePath);
|
||||||
} catch (Exception) { /* ignore */}
|
} catch (Exception) { /* ignore */}
|
||||||
var matchResult = Regex.Match(content, @"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)");
|
var matchResult = GamePathRegex().Match(content);
|
||||||
if (!matchResult.Success) {
|
if (!matchResult.Success) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe");
|
var entryName = matchResult.Groups["1"].Value.Replace("_Data", ".exe");
|
||||||
return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
return Path.GetFullPath(Path.Combine(matchResult.Value, "..", entryName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?m).:/.+(GenshinImpact_Data|YuanShen_Data)")]
|
||||||
|
private static partial Regex GamePathRegex();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,13 @@ using Proto;
|
|||||||
|
|
||||||
namespace YaeAchievement;
|
namespace YaeAchievement;
|
||||||
|
|
||||||
public class CacheFile {
|
public class CacheFile(string identifier) {
|
||||||
|
|
||||||
private readonly string _cacheName;
|
private readonly string _cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
|
||||||
private CacheItem? _content;
|
private CacheItem? _content;
|
||||||
|
|
||||||
public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
|
public DateTime LastWriteTime => Exists() ? File.GetLastWriteTimeUtc(_cacheName) : DateTime.UnixEpoch;
|
||||||
|
|
||||||
public CacheFile(string identifier) {
|
|
||||||
_cacheName = Path.Combine(GlobalVars.CachePath, $"{identifier.MD5Hash()[..16]}.miko");
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Exists() => File.Exists(_cacheName);
|
public bool Exists() => File.Exists(_cacheName);
|
||||||
|
|
||||||
public CacheItem Read() {
|
public CacheItem Read() {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System.Text.Json;
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using Proto;
|
using Proto;
|
||||||
using YaeAchievement.AppCenterSDK;
|
|
||||||
using YaeAchievement.res;
|
using YaeAchievement.res;
|
||||||
using static Proto.Achievement.Types;
|
using static Proto.Achievement.Types;
|
||||||
|
|
||||||
@@ -16,6 +15,10 @@ public static class Export {
|
|||||||
|
|
||||||
public static uint ExportTo { get; set; } = uint.MaxValue;
|
public static uint ExportTo { get; set; } = uint.MaxValue;
|
||||||
|
|
||||||
|
private static readonly JsonSerializerOptions JsonOpts = new () {
|
||||||
|
WriteIndented = true
|
||||||
|
};
|
||||||
|
|
||||||
public static void Choose(AchievementAllDataNotify data) {
|
public static void Choose(AchievementAllDataNotify data) {
|
||||||
if (ExportTo == uint.MaxValue) {
|
if (ExportTo == uint.MaxValue) {
|
||||||
Console.Write(App.ExportChoose);
|
Console.Write(App.ExportChoose);
|
||||||
@@ -40,16 +43,15 @@ public static class Export {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class CocogoatResponse {
|
private class CocogoatResponse {
|
||||||
[JsonPropertyName("key")] public string Code { get; set; } = null!;
|
[JsonPropertyName("key")] public string Code { get; init; } = null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ToCocogoat(AchievementAllDataNotify data) {
|
private static void ToCocogoat(AchievementAllDataNotify data) {
|
||||||
var result = JsonSerializer.Serialize(ExportToUIAFApp(data));
|
var result = JsonSerializer.Serialize(ExportToUIAFApp(data));
|
||||||
using var request = new HttpRequestMessage {
|
using var request = new HttpRequestMessage();
|
||||||
Method = HttpMethod.Post,
|
request.Method = HttpMethod.Post;
|
||||||
RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}"),
|
request.RequestUri = new Uri($"https://77.cocogoat.cn/v1/memo?source={App.AllAchievement}");
|
||||||
Content = new StringContent(result, Encoding.UTF8, "application/json")
|
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
|
||||||
};
|
|
||||||
using var response = Utils.CHttpClient.Send(request);
|
using var response = Utils.CHttpClient.Send(request);
|
||||||
if (response.StatusCode != HttpStatusCode.Created) {
|
if (response.StatusCode != HttpStatusCode.Created) {
|
||||||
Console.WriteLine(App.ExportToCocogoatFail);
|
Console.WriteLine(App.ExportToCocogoatFail);
|
||||||
@@ -68,11 +70,10 @@ public static class Export {
|
|||||||
{ "key", id },
|
{ "key", id },
|
||||||
{ "data", ExportToUIAFApp(data) }
|
{ "data", ExportToUIAFApp(data) }
|
||||||
});
|
});
|
||||||
using var request = new HttpRequestMessage {
|
using var request = new HttpRequestMessage();
|
||||||
Method = HttpMethod.Post,
|
request.Method = HttpMethod.Post;
|
||||||
RequestUri = new Uri("https://api.qyinter.com/achievementRedis"),
|
request.RequestUri = new Uri("https://api.qyinter.com/achievementRedis");
|
||||||
Content = new StringContent(result, Encoding.UTF8, "application/json")
|
request.Content = new StringContent(result, Encoding.UTF8, "application/json");
|
||||||
};
|
|
||||||
using var response = Utils.CHttpClient.Send(request);
|
using var response = Utils.CHttpClient.Send(request);
|
||||||
Console.WriteLine(App.ExportToWxApp1Success, id);
|
Console.WriteLine(App.ExportToWxApp1Success, id);
|
||||||
}
|
}
|
||||||
@@ -100,7 +101,7 @@ public static class Export {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void ToTeyvatGuide(AchievementAllDataNotify data) {
|
private static void ToTeyvatGuide(AchievementAllDataNotify data) {
|
||||||
if (Process.GetProcessesByName("TeyvatGuide").Any()) {
|
if (Process.GetProcessesByName("TeyvatGuide").Length != 0) {
|
||||||
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
Utils.CopyToClipboard(JsonSerializer.Serialize(ExportToUIAFApp(data)));
|
||||||
Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement");
|
Utils.ShellOpen("teyvatguide://import_uigf?app=YaeAchievement");
|
||||||
Console.WriteLine(App.ExportToTauriSuccess);
|
Console.WriteLine(App.ExportToTauriSuccess);
|
||||||
@@ -171,10 +172,10 @@ public static class Export {
|
|||||||
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
finishAt = DateTimeOffset.FromUnixTimeSeconds(ts).ToLocalTime().ToString("yyyy/MM/dd HH:mm:ss");
|
||||||
}
|
}
|
||||||
var current = ach.Status != Status.Unfinished ? ach.Current == 0 ? ach.Total : ach.Current : ach.Current;
|
var current = ach.Status != Status.Unfinished ? ach.Current == 0 ? ach.Total : ach.Current : ach.Current;
|
||||||
outList.Add(new List<object> {
|
outList.Add([
|
||||||
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
|
ach.Id, ach.Status.ToDesc(), achInfo.Group, achInfo.Name,
|
||||||
achInfo.Description, current, ach.Total, finishAt
|
achInfo.Description, current, ach.Total, finishAt
|
||||||
});
|
]);
|
||||||
}
|
}
|
||||||
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
|
var output = new List<string> { "ID,状态,特辑,名称,描述,当前进度,目标进度,完成时间" };
|
||||||
output.AddRange(outList.OrderBy(v => v[2]).Select(item => {
|
output.AddRange(outList.OrderBy(v => v[2]).Select(item => {
|
||||||
@@ -190,9 +191,7 @@ public static class Export {
|
|||||||
|
|
||||||
private static void ToRawJson(AchievementAllDataNotify data) {
|
private static void ToRawJson(AchievementAllDataNotify data) {
|
||||||
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json");
|
var path = Path.GetFullPath($"export-{DateTime.Now:yyyyMMddHHmmss}-raw.json");
|
||||||
var text = JsonSerializer.Serialize(data, new JsonSerializerOptions {
|
var text = JsonSerializer.Serialize(data, JsonOpts);
|
||||||
WriteIndented = true
|
|
||||||
});
|
|
||||||
if (TryWriteToFile(path, text)) {
|
if (TryWriteToFile(path, text)) {
|
||||||
Console.WriteLine(App.ExportToFileSuccess, path);
|
Console.WriteLine(App.ExportToFileSuccess, path);
|
||||||
}
|
}
|
||||||
@@ -220,6 +219,7 @@ public static class Export {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once InconsistentNaming
|
||||||
private static bool CheckWinUIAppScheme(string protocol) {
|
private static bool CheckWinUIAppScheme(string protocol) {
|
||||||
return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}";
|
return (string?)Registry.ClassesRoot.OpenSubKey(protocol)?.GetValue("") == $"URL:{protocol}";
|
||||||
}
|
}
|
||||||
@@ -228,7 +228,7 @@ public static class Export {
|
|||||||
return string.Join(separator, list);
|
return string.Join(separator, list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly List<uint> UnusedAchievement = new() { 84517 };
|
private static readonly List<uint> UnusedAchievement = [ 84517 ];
|
||||||
|
|
||||||
private static string ToDesc(this Status status) {
|
private static string ToDesc(this Status status) {
|
||||||
return status switch {
|
return status switch {
|
||||||
@@ -248,7 +248,6 @@ public static class Export {
|
|||||||
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
public static int PrintMsgAndReturnErrCode(this Win32Exception ex, string msg) {
|
||||||
// ReSharper disable once LocalizableElement
|
// ReSharper disable once LocalizableElement
|
||||||
Console.WriteLine($"{msg}: {ex.Message}");
|
Console.WriteLine($"{msg}: {ex.Message}");
|
||||||
AppCenter.TrackCrash(ex, false);
|
|
||||||
return ex.NativeErrorCode;
|
return ex.NativeErrorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Proto;
|
using Proto;
|
||||||
using YaeAchievement;
|
using YaeAchievement;
|
||||||
using YaeAchievement.AppCenterSDK;
|
|
||||||
using YaeAchievement.AppCenterSDK.Models;
|
|
||||||
using YaeAchievement.res;
|
using YaeAchievement.res;
|
||||||
using static YaeAchievement.Utils;
|
using static YaeAchievement.Utils;
|
||||||
|
|
||||||
@@ -22,13 +20,6 @@ AppConfig.Load(args.GetOrNull(0) ?? "auto");
|
|||||||
Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue;
|
Export.ExportTo = ToUIntOrNull(args.GetOrNull(1)) ?? uint.MaxValue;
|
||||||
|
|
||||||
CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2)));
|
CheckUpdate(ToBooleanOrFalse(args.GetOrNull(2)));
|
||||||
AppCenter.Init();
|
|
||||||
new EventLog("AppInit") {
|
|
||||||
Properties = {
|
|
||||||
{ "AppVersion", GlobalVars.AppVersionName },
|
|
||||||
{ "SystemVersion", DeviceHelper.GetSystemVersion() }
|
|
||||||
}
|
|
||||||
}.Enqueue();
|
|
||||||
|
|
||||||
var historyCache = new CacheFile("ExportData");
|
var historyCache = new CacheFile("ExportData");
|
||||||
|
|
||||||
|
|||||||
26
src/Utils.cs
26
src/Utils.cs
@@ -11,7 +11,6 @@ using Windows.Win32;
|
|||||||
using Windows.Win32.Foundation;
|
using Windows.Win32.Foundation;
|
||||||
using Windows.Win32.System.Console;
|
using Windows.Win32.System.Console;
|
||||||
using Proto;
|
using Proto;
|
||||||
using YaeAchievement.AppCenterSDK;
|
|
||||||
using YaeAchievement.res;
|
using YaeAchievement.res;
|
||||||
|
|
||||||
namespace YaeAchievement;
|
namespace YaeAchievement;
|
||||||
@@ -31,10 +30,9 @@ public static class Utils {
|
|||||||
|
|
||||||
public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) {
|
public static byte[] GetBucketFileAsByteArray(string path, bool cache = true) {
|
||||||
try {
|
try {
|
||||||
using var msg = new HttpRequestMessage {
|
using var msg = new HttpRequestMessage();
|
||||||
Method = HttpMethod.Get,
|
msg.Method = HttpMethod.Get;
|
||||||
RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}")
|
msg.RequestUri = new Uri($"{GlobalVars.BucketHost}/{path}");
|
||||||
};
|
|
||||||
var cacheFile = new CacheFile(path);
|
var cacheFile = new CacheFile(path);
|
||||||
if (cache && cacheFile.Exists()) {
|
if (cache && cacheFile.Exists()) {
|
||||||
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
|
msg.Headers.TryAddWithoutValidation("If-None-Match", $"{cacheFile.Read().Etag}");
|
||||||
@@ -73,11 +71,11 @@ public static class Utils {
|
|||||||
if (Native.OpenClipboard(HWND.Null))
|
if (Native.OpenClipboard(HWND.Null))
|
||||||
{
|
{
|
||||||
Native.EmptyClipboard();
|
Native.EmptyClipboard();
|
||||||
var hGlobal = (HANDLE) Marshal.AllocHGlobal((text.Length + 1) * 2);
|
var hGlobal = (HGLOBAL) Marshal.AllocHGlobal((text.Length + 1) * 2);
|
||||||
var hPtr = (nint) Native.GlobalLock(hGlobal);
|
var hPtr = (nint) Native.GlobalLock(hGlobal);
|
||||||
Marshal.Copy(text.ToCharArray(), 0, hPtr, text.Length);
|
Marshal.Copy(text.ToCharArray(), 0, hPtr, text.Length);
|
||||||
Native.GlobalUnlock(hPtr);
|
Native.GlobalUnlock((HGLOBAL) hPtr);
|
||||||
Native.SetClipboardData(13, hGlobal);
|
Native.SetClipboardData(13, new HANDLE(hPtr));
|
||||||
Marshal.FreeHGlobal(hGlobal);
|
Marshal.FreeHGlobal(hGlobal);
|
||||||
Native.CloseClipboard();
|
Native.CloseClipboard();
|
||||||
}
|
}
|
||||||
@@ -201,9 +199,6 @@ public static class Utils {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Console.WriteLine(ex.ToString());
|
Console.WriteLine(ex.ToString());
|
||||||
Console.WriteLine(App.UploadError);
|
|
||||||
AppCenter.TrackCrash((Exception) e.ExceptionObject);
|
|
||||||
AppCenter.Upload();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Environment.Exit(-1);
|
Environment.Exit(-1);
|
||||||
@@ -312,12 +307,9 @@ public static class Utils {
|
|||||||
}
|
}
|
||||||
await File.WriteAllBytesAsync(pkgPath, bytes);
|
await File.WriteAllBytesAsync(pkgPath, bytes);
|
||||||
Console.WriteLine(App.VcRuntimeInstalling);
|
Console.WriteLine(App.VcRuntimeInstalling);
|
||||||
using var process = new Process {
|
using var process = new Process();
|
||||||
StartInfo = {
|
process.StartInfo.FileName = pkgPath;
|
||||||
FileName = pkgPath,
|
process.StartInfo.Arguments = "/install /passive /norestart";
|
||||||
Arguments = "/install /passive /norestart"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
process.Start();
|
process.Start();
|
||||||
await process.WaitForExitAsync();
|
await process.WaitForExitAsync();
|
||||||
File.Delete(pkgPath);
|
File.Delete(pkgPath);
|
||||||
|
|||||||
Reference in New Issue
Block a user