diff --git a/BetterGenshinImpact.sln.DotSettings b/BetterGenshinImpact.sln.DotSettings
index 46e57701..8cc26dc7 100644
--- a/BetterGenshinImpact.sln.DotSettings
+++ b/BetterGenshinImpact.sln.DotSettings
@@ -1,2 +1,5 @@
- True
\ No newline at end of file
+ True
+ True
+ True
+ True
\ No newline at end of file
diff --git a/BetterGenshinImpact/Assets/Strings/md2html.html b/BetterGenshinImpact/Assets/Strings/md2html.html
index f559099d..429babf3 100644
--- a/BetterGenshinImpact/Assets/Strings/md2html.html
+++ b/BetterGenshinImpact/Assets/Strings/md2html.html
@@ -18,14 +18,10 @@
::-webkit-scrollbar-thumb:hover {
background: #555;
}
- .body {
- background-color: #202020;
- color: #e6edf3;
- }
.markdown-body {
margin: 0;
padding: 8px;
- background-color: #202020;
+ background-color: #202020 !important;
height: 100%
}
* {
diff --git a/BetterGenshinImpact/BetterGenshinImpact.csproj b/BetterGenshinImpact/BetterGenshinImpact.csproj
index 35f51ce0..80e7e7e4 100644
--- a/BetterGenshinImpact/BetterGenshinImpact.csproj
+++ b/BetterGenshinImpact/BetterGenshinImpact.csproj
@@ -48,6 +48,7 @@
+
diff --git a/BetterGenshinImpact/Helpers/Win32/CredentialManagerHelper.cs b/BetterGenshinImpact/Helpers/Win32/CredentialManagerHelper.cs
new file mode 100644
index 00000000..3bc4efa7
--- /dev/null
+++ b/BetterGenshinImpact/Helpers/Win32/CredentialManagerHelper.cs
@@ -0,0 +1,52 @@
+using System;
+using Meziantou.Framework.Win32;
+
+namespace BetterGenshinImpact.Helpers.Win32;
+
+public static class CredentialManagerHelper
+{
+ public static void SaveCredential(string applicationName, string userName, string secret, string comment,
+ CredentialPersistence persistence)
+ {
+ CredentialManager.WriteCredential(
+ applicationName: applicationName,
+ userName: userName,
+ secret: secret,
+ comment: comment,
+ persistence: persistence);
+ }
+
+ public static Credential? ReadCredential(string applicationName)
+ {
+ var credential = CredentialManager.ReadCredential(applicationName);
+ if (credential == null)
+ {
+ Console.WriteLine("No credential found.");
+ return null;
+ }
+
+ Console.WriteLine($"UserName: {credential.UserName}");
+ Console.WriteLine($"Secret: {credential.Password}");
+ Console.WriteLine($"Comment: {credential.Comment}");
+
+ return credential;
+ }
+
+ public static void UpdateCredential(string applicationName, string newUserName, string newSecret, string newComment)
+ {
+ SaveCredential(applicationName, newUserName, newSecret, newComment, CredentialPersistence.LocalMachine);
+ }
+
+ public static void DeleteCredential(string applicationName)
+ {
+ try
+ {
+ CredentialManager.DeleteCredential(applicationName);
+ Console.WriteLine("Credential deleted successfully.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error deleting credential: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs b/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs
new file mode 100644
index 00000000..0aff18d7
--- /dev/null
+++ b/BetterGenshinImpact/Helpers/Win32/MirrorChyanHelper.cs
@@ -0,0 +1,93 @@
+using System;
+using Windows.System;
+using BetterGenshinImpact.View.Windows;
+using Meziantou.Framework.Win32;
+using Wpf.Ui.Violeta.Controls;
+
+namespace BetterGenshinImpact.Helpers.Win32;
+
+public static class MirrorChyanHelper
+{
+ public static readonly string MirrorChyanCdkAppName = "KachinaInstaller_MirrorChyanCDK_BetterGI";
+
+
+ public static string? GetCdk()
+ {
+ var credential = CredentialManagerHelper.ReadCredential(MirrorChyanCdkAppName);
+ return credential?.Password;
+ }
+
+ public static string? GetAndPromptCdk()
+ {
+ var credential = CredentialManagerHelper.ReadCredential(MirrorChyanCdkAppName);
+ if (credential == null || credential.Password == null)
+ {
+ var cdk = PromptDialog.Prompt("Mirror酱是独立的第三方软件下载平台,提供付费的软件下载加速服务。\n如果你有 Mirror酱的 CDK,可以在这里输入。",
+ "请输入Mirror酱CDK",
+ string.Empty,
+ new PromptDialogConfig
+ {
+ ShowLeftButton = true,
+ LeftButtonText = "获取CDK",
+ LeftButtonClick = (sender, args) =>
+ {
+ Launcher.LaunchUriAsync(new Uri("https://mirrorchyan.com/zh/get-start"));
+ }
+ }
+ );
+ if (string.IsNullOrEmpty(cdk))
+ {
+ Toast.Warning("输入CDK为空,无法继续操作");
+ return null;
+ }
+
+ CredentialManagerHelper.SaveCredential(
+ MirrorChyanCdkAppName,
+ string.Empty,
+ cdk,
+ string.Empty,
+ CredentialPersistence.LocalMachine);
+ return cdk;
+ }
+ else
+ {
+ return credential.Password;
+ }
+ }
+
+ public static void EditCdk()
+ {
+ var credential = CredentialManagerHelper.ReadCredential(MirrorChyanCdkAppName);
+ var cdk = PromptDialog.Prompt("Mirror酱是独立的第三方软件下载平台,提供付费的软件下载加速服务。\n如果你有 Mirror酱的 CDK,可以在这里输入。",
+ "修改Mirror酱CDK",
+ credential?.Password!,
+ new PromptDialogConfig
+ {
+ ShowLeftButton = true,
+ LeftButtonText = "获取CDK",
+ LeftButtonClick = (sender, args) =>
+ {
+ Launcher.LaunchUriAsync(new Uri("https://mirrorchyan.com/zh/get-start"));
+ }
+ }
+ );
+ if (string.IsNullOrEmpty(cdk))
+ {
+ DeleteCdk();
+ }
+ else
+ {
+ CredentialManagerHelper.SaveCredential(
+ MirrorChyanCdkAppName,
+ string.Empty,
+ cdk,
+ string.Empty,
+ CredentialPersistence.LocalMachine);
+ }
+ }
+
+ public static void DeleteCdk()
+ {
+ CredentialManagerHelper.DeleteCredential(MirrorChyanCdkAppName);
+ }
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/Model/UpdateOption.cs b/BetterGenshinImpact/Model/UpdateOption.cs
index eb68bf53..7d4f0721 100644
--- a/BetterGenshinImpact/Model/UpdateOption.cs
+++ b/BetterGenshinImpact/Model/UpdateOption.cs
@@ -3,6 +3,8 @@
public sealed class UpdateOption
{
public UpdateTrigger Trigger { get; set; } = default;
+
+ public UpdateChannel Channel { get; set; } = UpdateChannel.Stable;
}
public enum UpdateTrigger
@@ -10,3 +12,10 @@ public enum UpdateTrigger
Auto,
Manual,
}
+
+
+public enum UpdateChannel
+{
+ Stable,
+ Alpha,
+}
\ No newline at end of file
diff --git a/BetterGenshinImpact/Service/Model/MirrorChyan/LatestResponse.cs b/BetterGenshinImpact/Service/Model/MirrorChyan/LatestResponse.cs
new file mode 100644
index 00000000..f6c099f5
--- /dev/null
+++ b/BetterGenshinImpact/Service/Model/MirrorChyan/LatestResponse.cs
@@ -0,0 +1,117 @@
+using System.Text.Json.Serialization;
+
+namespace BetterGenshinImpact.Service.Model.MirrorChyan;
+
+#nullable enable
+#pragma warning disable CS8618
+#pragma warning disable CS8601
+#pragma warning disable CS8603
+public partial class LatestResponse
+{
+ ///
+ /// 响应代码,https://github.com/MirrorChyan/docs/blob/main/ErrorCode.md
+ ///
+ [JsonPropertyName("code")]
+ public long Code { get; set; }
+
+ ///
+ /// 响应数据
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("data")]
+ public Data Data { get; set; }
+
+ ///
+ /// 响应信息
+ ///
+ [JsonPropertyName("msg")]
+ public string Msg { get; set; }
+}
+
+///
+/// 响应数据
+///
+public partial class Data
+{
+ ///
+ /// 更新包架构
+ ///
+ [JsonPropertyName("arch")]
+ public string Arch { get; set; }
+
+ ///
+ /// CDK过期时间戳
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("cdk_expired_time")]
+ public double? CdkExpiredTime { get; set; }
+
+ ///
+ /// 更新频道,stable | beta | alpha
+ ///
+ [JsonPropertyName("channel")]
+ public string Channel { get; set; }
+
+ ///
+ /// 自定义数据
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("custom_data")]
+ public string CustomData { get; set; }
+
+ ///
+ /// 文件大小
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("filesize")]
+ public long? Filesize { get; set; }
+
+ ///
+ /// 更新包系统
+ ///
+ [JsonPropertyName("os")]
+ public string Os { get; set; }
+
+ ///
+ /// 发版日志
+ ///
+ [JsonPropertyName("release_note")]
+ public string ReleaseNote { get; set; }
+
+ ///
+ /// sha256
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("sha256")]
+ public string Sha256 { get; set; }
+
+ ///
+ /// 更新包类型,incremental | full
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("update_type")]
+ public string UpdateType { get; set; }
+
+ ///
+ /// 下载地址
+ ///
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ [JsonPropertyName("url")]
+ public string Url { get; set; }
+
+ ///
+ /// 资源版本名称
+ ///
+ [JsonPropertyName("version_name")]
+ public string VersionName { get; set; }
+
+ ///
+ /// 资源版本号仅内部使用
+ ///
+ [JsonPropertyName("version_number")]
+ public long VersionNumber { get; set; }
+}
+
+#pragma warning restore CS8618
+#pragma warning restore CS8601
+#pragma warning restore CS8603
\ No newline at end of file
diff --git a/BetterGenshinImpact/Service/UpdateService.cs b/BetterGenshinImpact/Service/UpdateService.cs
index e5cf9d9c..3d720cb1 100644
--- a/BetterGenshinImpact/Service/UpdateService.cs
+++ b/BetterGenshinImpact/Service/UpdateService.cs
@@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
@@ -18,6 +19,8 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
+using BetterGenshinImpact.Service.Model.MirrorChyan;
+using Wpf.Ui.Violeta.Controls;
namespace BetterGenshinImpact.Service;
@@ -37,7 +40,7 @@ public class UpdateService : IUpdateService
_configService = configService;
Config = _configService.Get();
}
-
+
///
/// Please call me in main thread
@@ -50,13 +53,13 @@ public class UpdateService : IUpdateService
#if DEBUG && false
return;
#endif
- string newVersion = await GetLatestVersionAsync();
+ string newVersion = await GetLatestVersionAsync(option);
if (string.IsNullOrWhiteSpace(newVersion))
{
return;
}
-
+
// ---- 如果是调试模式且手动的检查更新的情况下,强制打开更新窗口 -----
// 方便调试窗口
if (RuntimeHelper.IsDebuggerAttached && option.Trigger == UpdateTrigger.Manual)
@@ -72,7 +75,7 @@ public class UpdateService : IUpdateService
{
await MessageBox.InformationAsync("当前已是最新版本!");
}
-
+
return;
}
@@ -87,7 +90,8 @@ public class UpdateService : IUpdateService
}
catch (Exception e)
{
- Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
+ Debug.WriteLine("获取最新版本信息失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" +
+ Environment.NewLine + e.Message);
_logger.LogWarning("获取 BetterGI 最新版本信息失败");
}
}
@@ -110,7 +114,14 @@ public class UpdateService : IUpdateService
break;
case CheckUpdateWindow.CheckUpdateWindowButton.OtherUpdate:
- Process.Start(new ProcessStartInfo(DownloadPageUrl) { UseShellExecute = true });
+ if (option.Channel == UpdateChannel.Stable)
+ {
+ Process.Start(new ProcessStartInfo(DownloadPageUrl) { UseShellExecute = true });
+ }
+ else
+ {
+ Process.Start(new ProcessStartInfo("https://github.com/babalae/better-genshin-impact/actions/workflows/publish.yml") { UseShellExecute = true });
+ }
break;
case CheckUpdateWindow.CheckUpdateWindowButton.Update:
@@ -122,12 +133,12 @@ public class UpdateService : IUpdateService
await MessageBox.ErrorAsync("更新程序不存在,请选择其他更新方式!");
return;
}
+
// 启动
Process.Start(updaterExePath, "-I");
-
+
// 退出程序
Application.Current.Shutdown();
-
}
break;
@@ -144,11 +155,118 @@ public class UpdateService : IUpdateService
}
};
- win.NavigateToHtml(await GetReleaseMarkdownHtmlAsync());
+ if (option.Channel == UpdateChannel.Stable)
+ {
+ win.NavigateToHtml(await GetReleaseMarkdownHtmlAsync());
+ }
+
win.ShowDialog();
}
- private async Task GetLatestVersionAsync()
+ private async Task GetLatestVersionAsync(UpdateOption option)
+ {
+ if (option.Channel == UpdateChannel.Stable)
+ {
+ return await UpdateFromOss();
+ }
+ else
+ {
+ return await UpdateFromMirrorChyan();
+ }
+ }
+
+ ///
+ /// 文档
+ /// https://apifox.com/apidoc/shared/ffdc8453-597d-4ba6-bd3c-5e375c10c789
+ ///
+ ///
+ private async Task UpdateFromMirrorChyan()
+ {
+ try
+ {
+ const string url = "https://mirrorchyan.com/api/resources/BGI/latest";
+ var queryParams = new Dictionary
+ {
+ { "user_agent", "BetterGI" },
+ { "os", "win" },
+ { "arch", "x64" },
+ { "channel", "alpha" }
+ };
+
+ using var httpClient = new HttpClient();
+
+ var finalUrl = $"{url}?{string.Join("&", queryParams.Select(x => $"{x.Key}={x.Value}"))}";
+ var response = await httpClient.GetAsync(finalUrl);
+ LatestResponse? result = null;
+ if (response.StatusCode == HttpStatusCode.OK)
+ {
+ response.EnsureSuccessStatusCode();
+ result = await response.Content.ReadFromJsonAsync();
+ }
+ else
+ {
+ // 即使是403、400也尝试读取响应体
+ var content = await response.Content.ReadAsStringAsync();
+ result = JsonConvert.DeserializeObject(content);
+ }
+
+ if (result != null)
+ {
+ if (result.Code == 0)
+ {
+ return result.Data.VersionName;
+ }
+ else if (result.Code < 0)
+ {
+ Toast.Error(
+ $"Mirror酱源更新检查失败,意料之外的严重错误,请及时联系 Mirror 酱的技术支持处理\n,错误代码:{result.Code},错误信息:{result.Msg}");
+ return string.Empty;
+ }
+ else
+ {
+ ToastError(result);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.LogDebug(e, "Mirror源更新检查失败");
+ Toast.Warning($"Mirror源更新检查失败,{e.Message}");
+
+ }
+
+ return string.Empty;
+ }
+
+ private static void ToastError(LatestResponse response)
+ {
+ if (response.Code == 7001)
+ {
+ Toast.Warning("Mirror酱 CDK 已过期,请重新获取CDK");
+ }
+ else if (response.Code == 7002)
+ {
+ Toast.Warning("Mirror酱 CDK 错误!");
+ }
+ else if (response.Code == 7003)
+ {
+ Toast.Warning("Mirror酱 CDK 今日下载次数已达上限");
+ }
+ else if (response.Code == 7004)
+ {
+ Toast.Warning("Mirror酱 CDK 类型和待下载的资源不匹配");
+ }
+ else if (response.Code == 7005)
+ {
+ Toast.Warning("Mirror酱 CDK 已被封禁");
+ }
+ else
+ {
+ Toast.Warning($"Mirror酱源更新检查失败,错误信息:{response.Msg}");
+ }
+ }
+
+ private async Task UpdateFromOss()
{
try
{
@@ -181,7 +299,9 @@ public class UpdateService : IUpdateService
{
using HttpClient httpClient = new();
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
- string jsonString = await httpClient.GetStringAsync("https://api.github.com/repos/babalae/better-genshin-impact/releases/latest");
+ string jsonString =
+ await httpClient.GetStringAsync(
+ "https://api.github.com/repos/babalae/better-genshin-impact/releases/latest");
var jsonDict = JsonConvert.DeserializeObject>(jsonString);
if (jsonDict != null)
@@ -191,7 +311,8 @@ public class UpdateService : IUpdateService
string md = $"# {name}{new string('\n', 2)}{body}";
md = WebUtility.HtmlEncode(md);
- string md2html = ResourceHelper.GetString($"pack://application:,,,/Assets/Strings/md2html.html", Encoding.UTF8);
+ string md2html = ResourceHelper.GetString($"pack://application:,,,/Assets/Strings/md2html.html",
+ Encoding.UTF8);
var html = md2html.Replace("{{content}}", md);
return html;
@@ -240,4 +361,4 @@ public class UpdateService : IUpdateService