Files
MaaAssistantArknights/src/MeoAsstGui/ViewModels/VersionUpdateViewModel.cs
2021-12-15 12:33:09 +08:00

460 lines
17 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// MeoAsstGui - A part of the MeoAssistantArknights project
// Copyright (C) 2021 MistEO and Contributors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using Microsoft.Toolkit.Uwp.Notifications;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Stylet;
using StyletIoC;
namespace MeoAsstGui
{
public class VersionUpdateViewModel : Screen
{
private IWindowManager _windowManager;
private IContainer _container;
public VersionUpdateViewModel(IContainer container, IWindowManager windowManager)
{
_container = container;
_windowManager = windowManager;
}
[DllImport("MeoAssistant.dll")] private static extern IntPtr AsstGetVersion();
private string _curVersion = Marshal.PtrToStringAnsi(AsstGetVersion());
private string _latestVersion;
private string _updateTag = ViewStatusStorage.Get("VersionUpdate.name", string.Empty);
public string UpdateTag
{
get
{
return _updateTag;
}
set
{
SetAndNotify(ref _updateTag, value);
ViewStatusStorage.Set("VersionUpdate.name", value);
}
}
private string _updateInfo = ViewStatusStorage.Get("VersionUpdate.body", string.Empty);
public string UpdateInfo
{
get
{
return _updateInfo;
}
set
{
SetAndNotify(ref _updateInfo, value);
ViewStatusStorage.Set("VersionUpdate.body", value);
}
}
private bool _isFirstBootAfterUpdate = Convert.ToBoolean(ViewStatusStorage.Get("VersionUpdate.firstboot", Boolean.FalseString));
public bool IsFirstBootAfterUpdate
{
get
{
return _isFirstBootAfterUpdate;
}
set
{
SetAndNotify(ref _isFirstBootAfterUpdate, value);
ViewStatusStorage.Set("VersionUpdate.firstboot", value.ToString());
}
}
private string _updatePackageName = ViewStatusStorage.Get("VersionUpdate.package", string.Empty);
public string UpdatePackageName
{
get
{
return _updatePackageName;
}
set
{
SetAndNotify(ref _updatePackageName, value);
ViewStatusStorage.Set("VersionUpdate.package", value.ToString());
}
}
private const string _requestUrl = "https://api.github.com/repos/MistEO/MeoAssistantArknights/releases/latest";
private const string _requestBetaUrl = "https://api.github.com/repos/MistEO/MeoAssistantArknights/releases";
private string _viewUrl;
private JObject _lastestJson;
private string _downloadUrl;
// 检查是否有已下载的更新包,如果有立即更新并重启进程
public bool CheckAndUpdateNow()
{
if (UpdateTag == string.Empty
|| UpdatePackageName == string.Empty
|| !File.Exists(UpdatePackageName))
{
return false;
}
new ToastContentBuilder()
.AddText("检测到新版本包,正在解压,请稍等……")
.AddText(UpdateTag)
.Show();
string extractDir = Directory.GetCurrentDirectory() + "\\NewVersionExtract";
// 解压
System.IO.Compression.ZipFile.ExtractToDirectory(UpdatePackageName, extractDir);
var uncopiedList = new List<string>();
// 复制新版本的所有文件到当前路径下
foreach (var file in Directory.GetFiles(extractDir))
{
try
{
File.Copy(file, Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(file)), true);
}
catch (Exception)
{
uncopiedList.Add(file);
}
}
foreach (var directory in Directory.GetDirectories(extractDir))
{
CopyFilesRecursively(directory, Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(directory)));
}
// 程序正在运行中,部分文件是无法覆写的,这里曲线操作下
// 先将当前这些文件重命名,然后再把新的复制过来
foreach (var oldfile in Directory.GetFiles(Directory.GetCurrentDirectory(), "*.old"))
{
File.Delete(oldfile);
}
foreach (var file in uncopiedList)
{
string curFileName = Path.Combine(Directory.GetCurrentDirectory(), Path.GetFileName(file));
File.Move(curFileName, curFileName + ".old");
File.Copy(file, curFileName);
}
// 操作完了,把解压的文件删了
Directory.Delete(extractDir, true);
File.Delete(UpdatePackageName);
// 保存更新信息,下次启动后会弹出已更新完成的提示
IsFirstBootAfterUpdate = true;
UpdatePackageName = string.Empty;
ViewStatusStorage.Save();
// 重启进程(启动的是更新后的程序了)
System.Diagnostics.Process newProcess = new System.Diagnostics.Process();
newProcess.StartInfo.FileName = System.AppDomain.CurrentDomain.FriendlyName;
newProcess.StartInfo.WorkingDirectory = Directory.GetCurrentDirectory();
newProcess.Start();
Application.Current.Shutdown();
return true;
}
// 检查更新,并下载更新包
public bool CheckAndDownloadUpdate()
{
// 检查更新
if (!CheckUpdate())
{
return false;
}
// 保存新版本的信息
UpdatePackageName = _downloadUrl.Substring(_downloadUrl.LastIndexOf('/') + 1);
UpdateTag = _lastestJson["name"].ToString();
UpdateInfo = _lastestJson["body"].ToString();
var openUrlToastButton = new ToastButton();
openUrlToastButton.SetContent("前往页面查看");
openUrlToastButton.SetProtocolActivation(new Uri(_viewUrl));
new ToastContentBuilder()
.AddText("检测到新版本,正在后台下载……")
.AddText(UpdateTag)
.AddText(UpdateInfo)
.AddButton(openUrlToastButton)
.Show();
// 下载压缩包
const int downloadRetryMaxTimes = 2;
string downloadTempFilename = UpdatePackageName + ".tmp";
bool downloaded = false;
for (int i = 0; i != downloadRetryMaxTimes; ++i)
{
if (DownloadFile(_downloadUrl.Replace("github.com", "hub.fastgit.org"), downloadTempFilename)
|| DownloadFile(_downloadUrl, downloadTempFilename))
{
downloaded = true;
break;
}
}
if (!downloaded)
{
new ToastContentBuilder()
.AddText("新版本下载失败")
.AddText("请尝试手动下载后将压缩包放到目录下_(:з」∠)_")
.AddButton(openUrlToastButton)
.Show();
return false;
}
File.Copy(downloadTempFilename, UpdatePackageName, true);
File.Delete(downloadTempFilename);
// 把相关信息存下来,更新完之后启动的时候显示
new ToastContentBuilder()
.AddText("新版本下载完成")
.AddText("软件将在下次启动时自动更新!")
.AddText("✿✿ヽ(°▽°)ノ✿")
.Show();
return true;
}
public bool CheckUpdate()
{
var settings = _container.Get<SettingsViewModel>();
string response = string.Empty;
const int requestRetryMaxTimes = 5;
for (int i = 0; i != requestRetryMaxTimes; ++i)
{
if (settings.UpdateBeta)
{
response = RequestApi(_requestBetaUrl);
}
else
{
response = RequestApi(_requestUrl);
}
if (response.Length != 0)
{
break;
}
}
if (response.Length == 0)
{
return false;
}
try
{
JObject json;
if (settings.UpdateBeta)
{
JArray all = (JArray)JsonConvert.DeserializeObject(response);
json = (JObject)all[0];
}
else
{
json = (JObject)JsonConvert.DeserializeObject(response);
}
_viewUrl = json["html_url"].ToString();
_latestVersion = json["tag_name"].ToString();
if (string.Compare(_latestVersion, _curVersion) <= 0
|| ViewStatusStorage.Get("VersionUpdate.Ignore", string.Empty) == _latestVersion)
{
return false;
}
foreach (JObject asset in json["assets"])
{
string downUrl = asset["browser_download_url"].ToString();
if (downUrl.Length != 0)
{
_downloadUrl = downUrl;
_lastestJson = json;
return true;
}
}
}
catch (Exception)
{
return false;
}
return false;
}
private string RequestApi(string url)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
httpWebRequest.Method = "GET";
httpWebRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36";
httpWebRequest.Accept = "application/vnd.github.v3+json";
var settings = _container.Get<SettingsViewModel>();
if (settings.Proxy.Length > 0)
{
httpWebRequest.Proxy = new WebProxy(settings.Proxy);
}
//httpWebRequest.Timeout = 20000;
try
{
HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream(), Encoding.UTF8);
string responseContent = streamReader.ReadToEnd();
streamReader.Close();
httpWebResponse.Close();
return responseContent;
}
catch (Exception)
{
return string.Empty;
}
}
private bool DownloadFile(string url, string filename)
{
string cmd = Environment.CurrentDirectory + "\\aria2c.exe";
string args = "-c " + url + " -o " + filename;
var settings = _container.Get<SettingsViewModel>();
if (settings.Proxy.Length > 0)
{
args += " --all-proxy " + settings.Proxy;
}
int exit_code = -1;
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = cmd;
startInfo.Arguments = args;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
Process processTemp = new Process();
processTemp.StartInfo = startInfo;
processTemp.EnableRaisingEvents = true;
try
{
processTemp.Start();
processTemp.WaitForExit();
exit_code = processTemp.ExitCode;
}
catch (Exception ex)
{
return false;
}
return exit_code == 0;
}
public bool ResourceOTA()
{
const string req_base_url = "https://api.github.com/repos/MistEO/MeoAssistantArknights/commits?path=";
const string down_base_url = "https://cdn.jsdelivr.net/gh/MistEO/MeoAssistantArknights@";
var update_dict = new Dictionary<string, string>()
{
{ "3rdparty/resource/penguin-stats-recognize/json/stages.json" , "resource/penguin-stats-recognize/json/stages.json"},
{ "resource/recruit.json", "resource/recruit.json" }
};
bool updated = false;
string message = "";
foreach (var item in update_dict)
{
string url = item.Key;
string filename = item.Value;
string cur_sha = ViewStatusStorage.Get(filename, string.Empty);
string response = RequestApi(req_base_url + url);
string cloud_sha;
string cur_message = "";
try
{
JArray arr = (JArray)JsonConvert.DeserializeObject(response);
JObject commit_info = (JObject)arr[0];
cloud_sha = commit_info["sha"].ToString();
cur_message = commit_info["commit"]["message"].ToString();
}
catch (Exception)
{
continue;
}
if (cur_sha == cloud_sha)
{
continue;
}
string tempname = filename + ".tmp";
string download_url = down_base_url + cloud_sha + "/" + url;
if (!DownloadFile(download_url, tempname))
{
continue;
}
string tmp = File.ReadAllText(tempname).Replace("\r\n", "\n");
try
{
JsonConvert.DeserializeObject(tmp);
}
catch (Exception)
{
continue;
}
string src = File.ReadAllText(filename).Replace("\r\n", "\n");
if (src.Length != tmp.Length)
{
File.Copy(tempname, filename, true);
updated = true;
message += cur_message + "\n";
}
ViewStatusStorage.Set(filename, cloud_sha);
File.Delete(tempname);
}
if (updated)
{
new ToastContentBuilder()
.AddText("资源已更新,重启软件生效!")
.AddText(message)
.Show();
return true;
}
else
{
return false;
}
}
private static void CopyFilesRecursively(string sourcePath, string targetPath)
{
//Now Create all of the directories
foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories))
{
Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath));
}
//Copy all the files & Replaces any files with the same name
foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories))
{
File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true);
}
}
public void Close()
{
RequestClose();
IsFirstBootAfterUpdate = false;
UpdateTag = "";
UpdateInfo = "";
}
}
}