Added SelfUpdate
This commit is contained in:
@@ -21,6 +21,19 @@ public static class Entry
|
|||||||
|
|
||||||
File.Delete("./Data/Resources/plugins.json");
|
File.Delete("./Data/Resources/plugins.json");
|
||||||
Directory.Delete("./Libraries/", true);
|
Directory.Delete("./Libraries/", true);
|
||||||
|
}),
|
||||||
|
|
||||||
|
new StartupAction("--update-cleanup", () => {
|
||||||
|
List<string> files = new List<string>();
|
||||||
|
files.AddRange(Directory.GetFiles("./"));
|
||||||
|
|
||||||
|
foreach (var file in files)
|
||||||
|
{
|
||||||
|
if (file.EndsWith(".bak"))
|
||||||
|
File.Delete(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.Delete("temp");
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
+6
-11
@@ -4,7 +4,7 @@ using System.Reflection;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using DiscordBot.Bot.Actions.Extra;
|
using DiscordBot.Bot.Actions.Extra;
|
||||||
|
using DiscordBot.Utilities;
|
||||||
using DiscordBotCore;
|
using DiscordBotCore;
|
||||||
using DiscordBotCore.Bot;
|
using DiscordBotCore.Bot;
|
||||||
using DiscordBotCore.Others;
|
using DiscordBotCore.Others;
|
||||||
@@ -83,17 +83,12 @@ public class Program
|
|||||||
{
|
{
|
||||||
await Application.CreateApplication();
|
await Application.CreateApplication();
|
||||||
|
|
||||||
AppUpdater updater = new();
|
AppUpdater updater = new AppUpdater();
|
||||||
var update = await updater.CheckForUpdates();
|
Update? update = await updater.PrepareUpdate();
|
||||||
|
if(update is not null)
|
||||||
if (update != Update.None)
|
|
||||||
{
|
{
|
||||||
Console.WriteLine($"New update available: {update.UpdateVersion}");
|
await ConsoleUtilities.ExecuteTaskWithBuiltInProgress(updater.SelfUpdate, update, "Discord Bot Update");
|
||||||
Console.WriteLine($"Download link: {update.UpdateUrl}");
|
return;
|
||||||
Console.WriteLine($"Update notes: {update.UpdateNotes}\n\n");
|
|
||||||
|
|
||||||
Console.WriteLine("Waiting 5 seconds ...");
|
|
||||||
await Task.Delay(5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Application.CurrentApplication.Logger.OnFormattedLog += (sender, logMessage) =>
|
Application.CurrentApplication.Logger.OnFormattedLog += (sender, logMessage) =>
|
||||||
|
|||||||
@@ -12,5 +12,11 @@ namespace DiscordBot
|
|||||||
this.Command = command;
|
this.Command = command;
|
||||||
this.RunAction = runAction;
|
this.RunAction = runAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StartupAction(string command, Action runAction)
|
||||||
|
{
|
||||||
|
this.Command = command;
|
||||||
|
this.RunAction = (args) => runAction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Updater.Application;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace DiscordBot.Utilities;
|
namespace DiscordBot.Utilities;
|
||||||
@@ -27,4 +28,22 @@ internal static class ConsoleUtilities
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task ExecuteTaskWithBuiltInProgress<T>(Func<T, IProgress<float>, Task> method, T parameter, string taskMessage)
|
||||||
|
{
|
||||||
|
await AnsiConsole.Progress()
|
||||||
|
.AutoClear(false) // Do not remove the task list when done
|
||||||
|
.HideCompleted(false) // Hide tasks as they are completed
|
||||||
|
.Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn())
|
||||||
|
.StartAsync(
|
||||||
|
async ctx =>
|
||||||
|
{
|
||||||
|
var task = ctx.AddTask(taskMessage);
|
||||||
|
IProgress<float> progress = new Progress<float>(x => task.Value = x);
|
||||||
|
await method(parameter, progress);
|
||||||
|
task.Value = 100;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,16 +5,13 @@ namespace DiscordBotCore.Interfaces.Updater
|
|||||||
{
|
{
|
||||||
public class AppVersion : IVersion
|
public class AppVersion : IVersion
|
||||||
{
|
{
|
||||||
public int Major { get; set; }
|
|
||||||
|
|
||||||
public int Minor { get; set; }
|
|
||||||
|
|
||||||
public int Patch { get; set; }
|
|
||||||
|
|
||||||
public int PatchVersion { get; set; }
|
|
||||||
|
|
||||||
public static readonly AppVersion CurrentAppVersion = new AppVersion(Assembly.GetEntryAssembly().GetName().Version.ToString());
|
public static readonly AppVersion CurrentAppVersion = new AppVersion(Assembly.GetEntryAssembly().GetName().Version.ToString());
|
||||||
|
|
||||||
|
public int Major { get; set; }
|
||||||
|
public int Minor { get; set; }
|
||||||
|
public int Patch { get; set; }
|
||||||
|
public int PatchVersion { get; set; }
|
||||||
|
|
||||||
private readonly char _Separator = '.';
|
private readonly char _Separator = '.';
|
||||||
|
|
||||||
public AppVersion(string versionAsString)
|
public AppVersion(string versionAsString)
|
||||||
|
|||||||
@@ -150,7 +150,10 @@ public static class ArchiveManager
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
entry.ExtractToFile(Path.Combine(folder, entry.FullName), true);
|
string path = Path.Combine(folder, entry.FullName);
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
|
||||||
|
entry.ExtractToFile(path, true);
|
||||||
currentSize += (ulong)entry.CompressedLength;
|
currentSize += (ulong)entry.CompressedLength;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -1,93 +1,126 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using DiscordBotCore.Interfaces.Updater;
|
using DiscordBotCore.Interfaces.Updater;
|
||||||
|
using DiscordBotCore.Online;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
namespace DiscordBotCore.Updater.Application
|
namespace DiscordBotCore.Updater.Application
|
||||||
{
|
{
|
||||||
public class AppUpdater
|
public class AppUpdater
|
||||||
{
|
{
|
||||||
private static readonly string _DefaultUpdateUrl = "https://github.com/andreitdr/SethDiscordBot/releases/latest";
|
|
||||||
|
const string ProjectName = "SethDiscordBot";
|
||||||
|
|
||||||
|
private static readonly string _DefaultUpdateUrl = $"https://github.com/andreitdr/{ProjectName}/releases/latest";
|
||||||
|
private static readonly string _DefaultUpdateDownloadUrl = $"https://github.com/andreitdr/{ProjectName}/releases/download/v";
|
||||||
|
|
||||||
|
private static readonly string _WindowsUpdateFile = "win-x64.zip";
|
||||||
|
private static readonly string _LinuxUpdateFile = "linux-x64.zip";
|
||||||
|
private static readonly string _MacOSUpdateFile = "osx-x64.zip";
|
||||||
|
|
||||||
|
private static readonly string _TempUpdateFolder = "temp";
|
||||||
|
|
||||||
private async Task<AppVersion> GetOnlineVersion()
|
private async Task<AppVersion> GetOnlineVersion()
|
||||||
{
|
{
|
||||||
HttpClient client = new HttpClient();
|
HttpClient client = new HttpClient();
|
||||||
var response = await client.GetAsync(_DefaultUpdateUrl);
|
var response = await client.GetAsync(_DefaultUpdateUrl);
|
||||||
|
|
||||||
if (response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
return AppVersion.CurrentAppVersion;
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
|
||||||
var version = Regex.Match(content, @"<title>.+?v(\d+\.\d+\.\d+.\d+).+?</title>").Groups[1].Value;
|
var url = response.RequestMessage.RequestUri.ToString();
|
||||||
|
var version = url.Split('/')[^1].Substring(1); // Remove the 'v' from the version number
|
||||||
|
|
||||||
return new AppVersion(version);
|
return new AppVersion(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppVersion.CurrentAppVersion;
|
private string GetDownloadUrl(AppVersion version)
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<Update> CheckForUpdates()
|
|
||||||
{
|
{
|
||||||
var latestVersion = await GetOnlineVersion();
|
string downloadUrl = _DefaultUpdateDownloadUrl;
|
||||||
if(latestVersion.IsNewerThan(AppVersion.CurrentAppVersion))
|
|
||||||
|
downloadUrl += $"{version.ToShortString()}/";
|
||||||
|
|
||||||
|
if(OperatingSystem.IsWindows())
|
||||||
|
downloadUrl += _WindowsUpdateFile;
|
||||||
|
else if (OperatingSystem.IsLinux())
|
||||||
|
downloadUrl += _LinuxUpdateFile;
|
||||||
|
else if (OperatingSystem.IsMacOS())
|
||||||
|
downloadUrl += _MacOSUpdateFile;
|
||||||
|
else
|
||||||
|
throw new PlatformNotSupportedException("Unsupported operating system");
|
||||||
|
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Update?> PrepareUpdate()
|
||||||
{
|
{
|
||||||
return new Update(AppVersion.CurrentAppVersion, latestVersion, _DefaultUpdateUrl, await GetUpdateNotes());
|
AppVersion currentVersion = AppVersion.CurrentAppVersion;
|
||||||
|
AppVersion newVersion = await GetOnlineVersion();
|
||||||
|
|
||||||
|
if(!newVersion.IsNewerThan(currentVersion))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
string downloadUrl = GetDownloadUrl(newVersion);
|
||||||
|
Update update = new Update(currentVersion, newVersion, downloadUrl);
|
||||||
|
|
||||||
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Update.None;
|
private void PrepareCurrentFolderForOverwrite()
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<string> GetUpdateNotes()
|
|
||||||
{
|
{
|
||||||
HttpClient client = new HttpClient();
|
List<string> files = new List<string>();
|
||||||
var response = await client.GetAsync(_DefaultUpdateUrl);
|
files.AddRange(Directory.GetFiles("./"));
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
File.Move(file, file + ".bak");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var content = await response.Content.ReadAsStringAsync();
|
private async Task CopyFolderContentOverCurrentFolder(string current, string otherFolder)
|
||||||
var markdownStart = content.IndexOf("<div data-pjax=\"true\" data-test-selector=\"body-content\"");
|
|
||||||
if(markdownStart == -1)
|
|
||||||
{
|
{
|
||||||
return string.Empty;
|
var files = Directory.GetFiles(otherFolder, "*.*", SearchOption.AllDirectories);
|
||||||
}
|
foreach (var file in files)
|
||||||
|
|
||||||
markdownStart = content.IndexOf(">", markdownStart) + 1; // Move past the opening tag
|
|
||||||
var markdownEnd = content.IndexOf("</div>", markdownStart);
|
|
||||||
var markdown = content.Substring(markdownStart, markdownEnd - markdownStart).Trim();
|
|
||||||
markdown = RemoveHtmlTags(markdown);
|
|
||||||
|
|
||||||
markdown = ApplyMarkdownFormatting(markdown);
|
|
||||||
|
|
||||||
return markdown;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private string RemoveHtmlTags(string text)
|
|
||||||
{
|
{
|
||||||
return Regex.Replace(text, "<.*?>", "").Trim();
|
string relativePath = file.Replace(otherFolder, "");
|
||||||
|
string newPath = current + relativePath;
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(newPath));
|
||||||
|
File.Copy(file, newPath);
|
||||||
}
|
}
|
||||||
private string ApplyMarkdownFormatting(string markdown)
|
}
|
||||||
|
|
||||||
|
public async Task SelfUpdate(Update update, IProgress<float> progress)
|
||||||
{
|
{
|
||||||
// Apply markdown formatting
|
Directory.CreateDirectory(_TempUpdateFolder);
|
||||||
markdown = markdown.Replace("**", "**"); // Bold
|
|
||||||
markdown = markdown.Replace("*", "*"); // Italic
|
string tempFile = $"./{_TempUpdateFolder}/update.zip";
|
||||||
markdown = markdown.Replace("`", "`"); // Inline code
|
string tempFolder = $"./{_TempUpdateFolder}/";
|
||||||
markdown = markdown.Replace("```", "```"); // Code block
|
|
||||||
markdown = markdown.Replace(">", ">"); // Greater than symbol
|
Directory.CreateDirectory(tempFolder);
|
||||||
markdown = markdown.Replace("<", "<"); // Less than symbol
|
|
||||||
markdown = markdown.Replace("&", "&"); // Ampersand
|
await ServerCom.DownloadFileAsync(update.UpdateUrl, tempFile, progress);
|
||||||
markdown = markdown.Replace(""", "\""); // Double quote
|
|
||||||
markdown = markdown.Replace("'", "'"); // Single quote
|
await ArchiveManager.ExtractArchive(tempFile, tempFolder, progress, UnzipProgressType.PERCENTAGE_FROM_TOTAL_SIZE);
|
||||||
markdown = markdown.Replace(" - ", "\n- "); // Convert bullet points to markdown list items
|
|
||||||
|
PrepareCurrentFolderForOverwrite();
|
||||||
|
|
||||||
|
if (OperatingSystem.IsWindows())
|
||||||
|
tempFolder += _WindowsUpdateFile;
|
||||||
|
else if (OperatingSystem.IsLinux())
|
||||||
|
tempFolder += _LinuxUpdateFile;
|
||||||
|
else if (OperatingSystem.IsMacOS())
|
||||||
|
tempFolder += _MacOSUpdateFile;
|
||||||
|
else throw new PlatformNotSupportedException();
|
||||||
|
|
||||||
|
await CopyFolderContentOverCurrentFolder("./", tempFolder.Substring(0, tempFolder.Length-4)); // Remove the .zip from the folder name
|
||||||
|
|
||||||
|
Process.Start("DiscordBot", "--update-cleanup");
|
||||||
|
Environment.Exit(0);
|
||||||
|
|
||||||
return markdown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,15 @@ namespace DiscordBotCore.Updater.Application
|
|||||||
{
|
{
|
||||||
public class Update
|
public class Update
|
||||||
{
|
{
|
||||||
public readonly static Update None = new Update(AppVersion.CurrentAppVersion, AppVersion.CurrentAppVersion, string.Empty, string.Empty);
|
public AppVersion NewVersion { get; private set; }
|
||||||
|
|
||||||
public AppVersion UpdateVersion { get; private set; }
|
|
||||||
public AppVersion CurrentVersion { get; private set; }
|
public AppVersion CurrentVersion { get; private set; }
|
||||||
public string UpdateUrl { get; private set; }
|
public string UpdateUrl { get; private set; }
|
||||||
public string UpdateNotes { get; private set; }
|
|
||||||
|
|
||||||
public Update(AppVersion currentVersion, AppVersion updateVersion, string updateUrl, string updateNotes)
|
public Update(AppVersion currentVersion, AppVersion updateVersion, string updateUrl)
|
||||||
{
|
{
|
||||||
UpdateVersion = updateVersion;
|
NewVersion = updateVersion;
|
||||||
CurrentVersion = currentVersion;
|
CurrentVersion = currentVersion;
|
||||||
UpdateUrl = updateUrl;
|
UpdateUrl = updateUrl;
|
||||||
UpdateNotes = updateNotes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user