feat: 添加 MirrorChyan 资源更新方式 (#11669)

Co-authored-by: MistEO <mistereo@hotmail.com>
This commit is contained in:
uye
2025-01-29 02:16:16 +08:00
committed by GitHub
parent 562da7d257
commit de138a888a
15 changed files with 252 additions and 12 deletions

View File

@@ -54,6 +54,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=ccast/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cdfend/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cguard/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Chyan/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cmedic/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Collapsal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=cpione/@EntryIndexedValue">True</s:Boolean>

View File

@@ -196,6 +196,8 @@ namespace MaaWpfGui.Constants
public const string UpdateProxy = "VersionUpdate.Proxy";
public const string ProxyType = "VersionUpdate.ProxyType";
public const string VersionType = "VersionUpdate.VersionType";
public const string ResourceUpdateSource = "VersionUpdate.ResourceUpdateSource";
public const string MirrorChyanCdk = "VersionUpdate.ResourceUpdateSource.MirrorChyanCdk";
public const string UpdateCheck = "VersionUpdate.UpdateCheck";
public const string UpdateAutoCheck = "VersionUpdate.ScheduledUpdateCheck";
public const string ResourceApi = "VersionUpdate.ResourceApi";

View File

@@ -82,5 +82,11 @@ namespace MaaWpfGui.Constants
"zh-tw" => $"{GitHubIssues}/new?assignees=&labels=bug&template=cn-bug-report.yaml",
_ => $"{GitHubIssues}/new?assignees=&labels=bug&template=en-bug-report.yaml",
};
// 资源更新更新源
public const string GithubResourceUpdate = "https://github.com/MaaAssistantArknights/MaaResource/archive/refs/heads/main.zip";
public const string MirrorChyanWebsite = "https://mirrorc.top";
public const string MirrorChyanResourceUpdate = $"{MirrorChyanWebsite}/api/resources/MaaResource/latest";
}
}

View File

@@ -20,19 +20,19 @@ namespace MaaWpfGui.Extensions
{
private static readonly ILogger _logger = Serilog.Log.ForContext("SourceContext", "HttpResponseLoggingExtension");
public static void Log(this HttpResponseMessage response)
public static void Log(this HttpResponseMessage response, bool logUri = true)
{
var method = response.RequestMessage.Method;
var url = response.RequestMessage.RequestUri.ToString();
var statusCode = response.StatusCode.ToString();
var method = response?.RequestMessage?.Method;
var url = response?.RequestMessage?.RequestUri?.ToString();
var statusCode = response?.StatusCode.ToString();
if (response.IsSuccessStatusCode)
if (response is { IsSuccessStatusCode: true })
{
_logger.Information("HTTP: {StatusCode} {Method} {Url}", statusCode, method, url);
_logger.Information("HTTP: {StatusCode} {Method} {Url}", statusCode, method, logUri ? url : "*****");
}
else
{
_logger.Warning("HTTP: {StatusCode} {Method} {Url}", statusCode, method, url);
_logger.Warning("HTTP: {StatusCode} {Method} {Url}", statusCode, method, logUri ? url : "*****");
}
}
}

View File

@@ -19,6 +19,8 @@ using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text.Json;
using System.Threading.Tasks;
using System.Web;
using MaaWpfGui.Constants;
@@ -26,6 +28,10 @@ using MaaWpfGui.Helper;
using MaaWpfGui.Main;
using MaaWpfGui.ViewModels;
using MaaWpfGui.ViewModels.UI;
using MaaWpfGui.ViewModels.UserControl.Settings;
using MaaWpfGui.Views.UI;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Serilog;
using Stylet;
@@ -355,11 +361,14 @@ namespace MaaWpfGui.Models
case UpdateResult.Success
when !await HttpResponseHelper.SaveResponseToFileAsync(response, saveTo):
return UpdateResult.Failed;
case UpdateResult.Success:
ETagCache.Set(response);
return UpdateResult.Success;
case UpdateResult.NotModified:
return UpdateResult.NotModified;
case UpdateResult.Failed:
default:
await Task.Delay(5000);
@@ -423,7 +432,7 @@ namespace MaaWpfGui.Models
public static async Task<bool> UpdateFromGithubAsync()
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceUpdating"));
bool download = await DownloadFullPackageAsync("https://github.com/MaaAssistantArknights/MaaResource/archive/refs/heads/main.zip", "MaaResource.zip").ConfigureAwait(false);
bool download = await DownloadFullPackageAsync(MaaUrls.GithubResourceUpdate, "MaaResource.zip").ConfigureAwait(false);
if (!download)
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceFailed"));
@@ -482,6 +491,118 @@ namespace MaaWpfGui.Models
return true;
}
// 从 MirrorChyan 下载完整包
public static async Task<bool> UpdateFromMirrorChyanAsync()
{
// https://mirrorc.top/api/resources/MaaResource/latest?current_version=<当前版本日期,从 version.json 里拿时间戳>&cdk=<cdk>&sp_id=<唯一识别码>
// 响应格式为 {"code":0,"msg":"success","data":{"version_name":"2025-01-22 14:28:32.839","version_number":9,"url":"<增量更新网址>"}}
var versionFilePath = Path.Combine(Environment.CurrentDirectory, "MirrorChyanResourceVersion");
string? currentVersion = null;
if (File.Exists(versionFilePath))
{
try
{
currentVersion = await File.ReadAllTextAsync(versionFilePath);
currentVersion = DateTime.Parse(currentVersion).ToString("yyyy-MM-dd+HH:mm:ss");
}
catch (Exception e)
{
_logger.Error("Failed to read or parse version file: " + e.Message);
currentVersion = null;
}
}
else
{
_logger.Information("Version file not found, assuming first time download.");
}
var cdk = SettingsViewModel.VersionUpdateSettings.MirrorChyanCdk;
var url = currentVersion == null
? $"{MaaUrls.MirrorChyanResourceUpdate}?cdk={cdk}"
: $"{MaaUrls.MirrorChyanResourceUpdate}?current_version={currentVersion}&cdk={cdk}";
var response = await Instances.HttpService.GetAsync(new(url), logUri: false);
if (response is null)
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceFailed"));
return false;
}
var json = await response.Content.ReadAsStringAsync();
var data = (JObject?)JsonConvert.DeserializeObject(json);
if (data is null)
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceFailed"));
return false;
}
var code = data["code"]?.ToString();
if (code is not null && code != "0")
{
ToastNotification.ShowDirect(data["msg"]?.ToString());
return false;
}
url = data["data"]?["url"]?.ToString();
if (string.IsNullOrEmpty(url))
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("AlreadyLatest"));
return false;
}
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceUpdating"));
bool download = await DownloadFullPackageAsync(url, "MaaResource.zip").ConfigureAwait(false);
if (!download)
{
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceFailed"));
return false;
}
// 解压到 MaaResource 文件夹
try
{
if (Directory.Exists("MaaResource"))
{
Directory.Delete("MaaResource", true);
}
ZipFile.ExtractToDirectory("MaaResource.zip", "MaaResource");
}
catch (Exception e)
{
_logger.Error("Failed to extract MaaResource.zip: " + e.Message);
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceFailed"));
return false;
}
// 没有 cache 文件夹,直接复制 resource 文件夹复制到当前目录
try
{
string sourcePath = new("MaaResource");
string destinationPath = Directory.GetCurrentDirectory();
DirectoryMerge(sourcePath, destinationPath);
}
catch (Exception e)
{
_logger.Error("Failed to copy folders: " + e.Message);
ToastNotification.ShowDirect(LocalizationHelper.GetString("GameResourceFailed"));
return false;
}
// 删除 MaaResource 文件夹 和 MaaResource.zip
try
{
Directory.Delete("MaaResource", true);
File.Delete("MaaResource.zip");
}
catch (Exception e)
{
_logger.Error("Failed to delete MaaResource: " + e.Message);
}
return true;
}
private static async Task<bool> DownloadFullPackageAsync(string url, string saveTo)
{
using var response = await Instances.HttpService.GetAsync(

View File

@@ -240,6 +240,8 @@ You can cancel this popup by clicking the Settings - About Us - Issue Reporting
<system:String x:Key="UpdateCheckNow">Check update</system:String>
<system:String x:Key="ShowChangelog">Changelog</system:String>
<system:String x:Key="ResourceUpdate">Resource update</system:String>
<system:String x:Key="ResourceUpdateSource">Resource Update Source</system:String>
<system:String x:Key="MirrorChyan">Mirror-Chyan</system:String>
<system:String x:Key="NewVersionFoundTitle">New Version Found</system:String>
<system:String x:Key="NewVersionFoundDescDownloading">Downloading in the background……</system:String>
<system:String x:Key="NewVersionFoundDescId" xml:space="preserve">Version: </system:String>

View File

@@ -240,6 +240,8 @@
<system:String x:Key="UpdateCheckNow">アップデートを確認する</system:String>
<system:String x:Key="ShowChangelog">変更履歴</system:String>
<system:String x:Key="ResourceUpdate">リソース更新</system:String>
<system:String x:Key="ResourceUpdateSource">リソース更新ソース</system:String>
<system:String x:Key="MirrorChyan">ミラーちゃん</system:String>
<system:String x:Key="NewVersionFoundTitle">新しいバージョンが見つかりました</system:String>
<system:String x:Key="NewVersionFoundDescDownloading">バックグラウンドでダウンロードを行います……</system:String>
<system:String x:Key="NewVersionFoundDescId" xml:space="preserve">バージョン: </system:String>

View File

@@ -240,6 +240,8 @@
<system:String x:Key="UpdateCheckNow">지금 업데이트</system:String>
<system:String x:Key="ShowChangelog">변경 내역</system:String>
<system:String x:Key="ResourceUpdate">리소스 업데이트</system:String>
<system:String x:Key="ResourceUpdateSource">리소스 업데이트 소스</system:String>
<system:String x:Key="MirrorChyan">미러짱</system:String>
<system:String x:Key="NewVersionFoundTitle">새로운 버전 확인</system:String>
<system:String x:Key="NewVersionFoundDescDownloading">백그라운드에서 다운로드 중……</system:String>
<system:String x:Key="NewVersionFoundDescId" xml:space="preserve">버전: </system:String>

View File

@@ -243,6 +243,8 @@
<system:String x:Key="UpdateCheckNow">检查更新</system:String>
<system:String x:Key="ShowChangelog">更新日志</system:String>
<system:String x:Key="ResourceUpdate">资源更新</system:String>
<system:String x:Key="ResourceUpdateSource">资源更新更新源</system:String>
<system:String x:Key="MirrorChyan">Mirror 酱</system:String>
<system:String x:Key="NewVersionFoundTitle">检测到新版本</system:String>
<system:String x:Key="NewVersionFoundDescDownloading">正在后台下载……</system:String>
<system:String x:Key="NewVersionFoundDescId" xml:space="preserve">版本号: </system:String>

View File

@@ -240,6 +240,8 @@
<system:String x:Key="UpdateCheckNow">檢查更新</system:String>
<system:String x:Key="ShowChangelog">更新日誌</system:String>
<system:String x:Key="ResourceUpdate">資源更新</system:String>
<system:String x:Key="ResourceUpdateSource">資源更新來源</system:String>
<system:String x:Key="MirrorChyan">Mirror 酱</system:String>
<system:String x:Key="NewVersionFoundTitle">檢測到新版本</system:String>
<system:String x:Key="NewVersionFoundDescDownloading">正在後臺下載……</system:String>
<system:String x:Key="NewVersionFoundDescId" xml:space="preserve">版本號: </system:String>

View File

@@ -131,7 +131,7 @@ namespace MaaWpfGui.Services.Web
return await response.Content.ReadAsStreamAsync();
}
public async Task<HttpResponseMessage?> GetAsync(Uri uri, Dictionary<string, string>? extraHeader = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead)
public async Task<HttpResponseMessage?> GetAsync(Uri uri, Dictionary<string, string>? extraHeader = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, bool logUri = true)
{
try
{
@@ -146,7 +146,7 @@ namespace MaaWpfGui.Services.Web
}
var response = await _client.SendAsync(request, httpCompletionOption);
response.Log();
response.Log(logUri);
return response;
}

View File

@@ -53,8 +53,9 @@ namespace MaaWpfGui.Services.Web
/// <param name="uri">Target Uri</param>
/// <param name="extraHeader">Extra HTTP Request Headers</param>
/// <param name="httpCompletionOption">The HTTP completion option</param>
/// <param name="logUri">Whether to log uri</param>
/// <returns><see cref="HttpRequestMessage"/> object</returns>
Task<HttpResponseMessage?> GetAsync(Uri uri, Dictionary<string, string>? extraHeader = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead);
Task<HttpResponseMessage?> GetAsync(Uri uri, Dictionary<string, string>? extraHeader = null, HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead, bool logUri = true);
/// <summary>
/// Send HTTP POST request and a string response

View File

@@ -26,6 +26,7 @@ using System.Windows;
using MaaWpfGui.Constants;
using MaaWpfGui.Helper;
using MaaWpfGui.Main;
using MaaWpfGui.Models;
using MaaWpfGui.Services;
using MaaWpfGui.States;
using Newtonsoft.Json;
@@ -465,6 +466,22 @@ public class VersionUpdateViewModel : Screen
}
*/
// 可以用 MirrorChyan 资源更新了喵
switch (SettingsViewModel.VersionUpdateSettings.ResourceUpdateSource)
{
case "MirrorChyan":
if (!string.IsNullOrEmpty(SettingsViewModel.VersionUpdateSettings.MirrorChyanCdk))
{
if (await ResourceUpdater.UpdateFromMirrorChyanAsync())
{
SettingsViewModel.VersionUpdateSettings.IsCheckingForUpdates = false;
return CheckUpdateRetT.OnlyGameResourceUpdated;
}
}
break;
}
SettingsViewModel.VersionUpdateSettings.IsCheckingForUpdates = false;
return ret;
}

View File

@@ -228,6 +228,39 @@ public class VersionUpdateSettingsUserControlModel : PropertyChangedBase
private bool _updateCheck = Convert.ToBoolean(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.UpdateCheck, bool.TrueString));
public List<GenericCombinedData<string>> ResourceUpdateSourceList { get; } = [
new() { Display = "Github", Value = "Github" },
new() { Display = LocalizationHelper.GetString("MirrorChyan"), Value = "MirrorChyan" },
];
private string _resourceUpdateSource = ConfigurationHelper.GetGlobalValue(ConfigurationKeys.ResourceUpdateSource, "Github");
/// <summary>
/// Gets or sets the type of version to update.
/// </summary>
public string ResourceUpdateSource
{
get => _resourceUpdateSource;
set
{
SetAndNotify(ref _resourceUpdateSource, value);
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.ResourceUpdateSource, value.ToString());
}
}
private string _mirrorChyanCdk = SimpleEncryptionHelper.Decrypt(ConfigurationHelper.GetGlobalValue(ConfigurationKeys.MirrorChyanCdk, string.Empty));
public string MirrorChyanCdk
{
get => _mirrorChyanCdk;
set
{
SetAndNotify(ref _mirrorChyanCdk, value);
value = SimpleEncryptionHelper.Encrypt(value);
ConfigurationHelper.SetGlobalValue(ConfigurationKeys.MirrorChyanCdk, value);
}
}
/// <summary>
/// Gets or sets a value indicating whether to check update.
/// </summary>
@@ -392,7 +425,26 @@ public class VersionUpdateSettingsUserControlModel : PropertyChangedBase
public async Task ManualUpdateResource()
{
IsCheckingForUpdates = true;
if (await ResourceUpdater.UpdateFromGithubAsync())
var success = false;
switch (ResourceUpdateSource)
{
case "Github":
if (await ResourceUpdater.UpdateFromGithubAsync())
{
success = true;
}
break;
case "MirrorChyan":
if (await ResourceUpdater.UpdateFromMirrorChyanAsync())
{
success = true;
}
break;
}
if (success)
{
if (AutoInstallUpdatePackage)
{

View File

@@ -3,6 +3,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:constants="clr-namespace:MaaWpfGui.Constants"
xmlns:controls="clr-namespace:MaaWpfGui.Styles.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:dd="urn:gong-wpf-dragdrop"
@@ -72,6 +73,35 @@
ItemsSource="{Binding VersionTypeList}"
SelectedValue="{Binding VersionType}"
SelectedValuePath="Value" />
<StackPanel>
<hc:ComboBox
Width="150"
Margin="10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
hc:TitleElement.Title="{DynamicResource ResourceUpdateSource}"
DisplayMemberPath="Display"
ItemsSource="{Binding ResourceUpdateSourceList}"
SelectedValue="{Binding ResourceUpdateSource}"
SelectedValuePath="Value" />
<StackPanel Visibility="{c:Binding 'ResourceUpdateSource == &quot;MirrorChyan&quot;'}">
<hc:TextBox
Width="150"
Margin="10"
HorizontalAlignment="Left"
VerticalAlignment="Center"
hc:TitleElement.Title="CDK"
Text="{Binding MirrorChyanCdk}" />
<controls:TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">
<Hyperlink
Cursor="Hand"
NavigateUri="{Binding Source={x:Static constants:MaaUrls.MirrorChyanWebsite}}"
TextDecorations="None">
<TextBlock Text="{DynamicResource MirrorChyan}" />
</Hyperlink>
</controls:TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
<StackPanel