diff --git a/DiscordBot/DiscordBot.csproj b/DiscordBot/DiscordBot.csproj index 868d4d2..fde69a4 100644 --- a/DiscordBot/DiscordBot.csproj +++ b/DiscordBot/DiscordBot.csproj @@ -3,11 +3,11 @@ Exe net8.0 disable - - + + False True - 1.0.3.1 + 1.0.3.0 none @@ -16,28 +16,28 @@ none - - - + + + - - - + + + - - - - - + + + + + - - - + + + - + \ No newline at end of file diff --git a/DiscordBot/Installer.cs b/DiscordBot/Installer.cs index 3f51ad9..d38381e 100644 --- a/DiscordBot/Installer.cs +++ b/DiscordBot/Installer.cs @@ -7,24 +7,30 @@ namespace DiscordBot; public static class Installer { - public static async Task GenerateStartupConfig() + private static async Task AskForConfig(string key, string message) { - var token = AnsiConsole.Ask("[green]Token:[/]"); - var botPrefix = AnsiConsole.Ask("[yellow]Prefix:[/]"); - var serverId = AnsiConsole.Ask("[deeppink1]Server ID:[/]"); + var value = AnsiConsole.Ask($"[green]{message}[/]"); - if (string.IsNullOrWhiteSpace(serverId)) serverId = string.Empty; - - if (string.IsNullOrWhiteSpace(token) || string.IsNullOrWhiteSpace(botPrefix)) + if (string.IsNullOrWhiteSpace(value)) { - AnsiConsole.MarkupLine("Invalid token or prefix !"); + AnsiConsole.MarkupLine($"Invalid {key} !"); Environment.Exit(-20); } - Config.AppSettings.Add("token", token); - Config.AppSettings.Add("prefix", botPrefix); - Config.AppSettings.Add("ServerID", serverId); + Config.AppSettings.Add(key, value); + } + public static async Task GenerateStartupConfig() + { + + if(!Config.AppSettings.ContainsKey("token")) + await AskForConfig("token", "Token:"); + + if(!Config.AppSettings.ContainsKey("prefix")) + await AskForConfig("prefix", "Prefix:"); + + if(!Config.AppSettings.ContainsKey("ServerID")) + await AskForConfig("ServerID", "Server ID:"); await Config.AppSettings.SaveToFile(); diff --git a/DiscordBot/Program.cs b/DiscordBot/Program.cs index abf8f01..7cf1741 100644 --- a/DiscordBot/Program.cs +++ b/DiscordBot/Program.cs @@ -23,6 +23,8 @@ public class Program { PreLoadComponents(args).Wait(); + + if (!AppSettings.ContainsKey("ServerID") || !AppSettings.ContainsKey("token") || !AppSettings.ContainsKey("prefix")) Installer.GenerateStartupConfig().Wait(); @@ -38,7 +40,6 @@ public class Program internalActionManager.Execute("plugin", "load").Wait(); internalActionManager.Refresh().Wait(); - while (true) { var cmd = Console.ReadLine(); @@ -61,7 +62,7 @@ public class Program Console.Clear(); Console.ForegroundColor = ConsoleColor.DarkYellow; - Console.WriteLine($"Running on version: {Assembly.GetExecutingAssembly().GetName().Version}"); + Console.WriteLine($"Running on version: {AppSettings["Version"]}"); Console.WriteLine("Git SethBot: https://github.com/andreitdr/SethDiscordBot"); Console.WriteLine("Git Plugins: https://github.com/andreitdr/SethPlugins"); @@ -111,6 +112,20 @@ public class Program { await Initialize(); + AppSettings["Version"] = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + + PluginManager.Updater.Application.AppUpdater updater = new(); + var update = await updater.CheckForUpdates(); + + if (update != PluginManager.Updater.Application.Update.None) + { + Console.WriteLine($"New update available: {update.UpdateVersion}"); + Console.WriteLine($"Download link: {update.UpdateUrl}"); + Console.WriteLine($"Update notes: {update.UpdateNotes}\n\n"); + + Environment.Exit(0); + } + Logger.OnLog += (sender, logMessage) => { var messageColor = logMessage.Type switch @@ -130,7 +145,5 @@ public class Program AnsiConsole.MarkupLine($"{messageColor}{logMessage.ThrowTime} {logMessage.Message} [/]"); }; - - AppSettings["Version"] = Assembly.GetExecutingAssembly().GetName().Version.ToString(); } } diff --git a/PluginManager/Bot/Boot.cs b/PluginManager/Bot/Boot.cs index 6f02ce3..6e749c9 100644 --- a/PluginManager/Bot/Boot.cs +++ b/PluginManager/Bot/Boot.cs @@ -107,9 +107,8 @@ public class Boot if (arg.Message.Contains("401")) { Config.AppSettings.Remove("token"); - Config.Logger.Log("The token is invalid. Please restart the bot and enter a valid token.", typeof(Boot), LogType.CRITICAL); + Config.Logger.Log("The token is invalid. Please restart the bot and follow the instructions", typeof(Boot), LogType.CRITICAL); await Config.AppSettings.SaveToFile(); - await Task.Delay(4000); Environment.Exit(0); } } @@ -123,7 +122,6 @@ public class Boot private Task Ready() { IsReady = true; - // UxHandler.ShowNotification("SethBot", "Seth Discord Bot is now up and running !").Wait(); return Task.CompletedTask; } diff --git a/PluginManager/Config.cs b/PluginManager/Config.cs index 4436580..c195567 100644 --- a/PluginManager/Config.cs +++ b/PluginManager/Config.cs @@ -1,12 +1,16 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using System.Threading.Tasks; using PluginManager.Bot; +using PluginManager.Interfaces.Updater; using PluginManager.Online; using PluginManager.Others; +using PluginManager.Others.Actions; using PluginManager.Others.Logger; using PluginManager.Plugin; +using PluginManager.Updater.Application; namespace PluginManager; @@ -26,6 +30,8 @@ public class Config public static Logger Logger; public static SettingsDictionary AppSettings; + public static InternalActionManager InternalActionManager; + public static PluginsManager PluginsManager; internal static Boot? DiscordBotClient; @@ -61,7 +67,6 @@ public class Config Logger = new Logger(false, true, _LogsFolder + $"/{DateTime.Today.ToShortDateString().Replace("/", "")}.log"); - PluginsManager = new PluginsManager(_DefaultBranchForPlugins); await PluginsManager.UninstallMarkedPlugins(); diff --git a/PluginManager/Interfaces/Updater/AppVersion.cs b/PluginManager/Interfaces/Updater/AppVersion.cs new file mode 100644 index 0000000..e522071 --- /dev/null +++ b/PluginManager/Interfaces/Updater/AppVersion.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace PluginManager.Interfaces.Updater +{ + 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(Config.AppSettings["Version"]); + + private readonly char _Separator = '.'; + + public AppVersion(string versionAsString) + { + string[] versionParts = versionAsString.Split(_Separator); + + if (versionParts.Length != 4) + { + throw new ArgumentException("Invalid version string"); + } + + Major = int.Parse(versionParts[0]); + Minor = int.Parse(versionParts[1]); + Patch = int.Parse(versionParts[2]); + PatchVersion = int.Parse(versionParts[3]); + } + + public bool IsNewerThan(IVersion version) + { + if (Major > version.Major) + return true; + + if (Major == version.Major && Minor > version.Minor) + return true; + + if (Major == version.Major && Minor == version.Minor && Patch > version.Patch) + return true; + + if (Major == version.Major && Minor == version.Minor && Patch == version.Patch && PatchVersion > version.PatchVersion) + return true; + + return false; + } + + public bool IsOlderThan(IVersion version) + { + if (Major < version.Major) + return true; + + if (Major == version.Major && Minor < version.Minor) + return true; + + if (Major == version.Major && Minor == version.Minor && Patch < version.Patch) + return true; + + if (Major == version.Major && Minor == version.Minor && Patch == version.Patch && PatchVersion < version.PatchVersion) + return true; + + return false; + } + + public bool IsEqualTo(IVersion version) + { + return Major == version.Major && Minor == version.Minor && Patch == version.Patch && PatchVersion == version.PatchVersion; + } + + public string ToShortString() + { + return $"{Major}.{Minor}.{Patch}.{PatchVersion}"; + } + + public override string ToString() + { + return ToShortString(); + } + } +} diff --git a/PluginManager/Interfaces/Updater/IVersion.cs b/PluginManager/Interfaces/Updater/IVersion.cs index 2d86a98..911ab98 100644 --- a/PluginManager/Interfaces/Updater/IVersion.cs +++ b/PluginManager/Interfaces/Updater/IVersion.cs @@ -5,6 +5,7 @@ public interface IVersion public int Major { get; } public int Minor { get; } public int Patch { get; } + public int PatchVersion => 0; public bool IsNewerThan(IVersion version); diff --git a/PluginManager/Interfaces/Updater/Version.cs b/PluginManager/Interfaces/Updater/Version.cs index 7d61402..36bda8d 100644 --- a/PluginManager/Interfaces/Updater/Version.cs +++ b/PluginManager/Interfaces/Updater/Version.cs @@ -68,4 +68,9 @@ public abstract class Version: IVersion { return $"{Major}.{Minor}.{Patch}"; } + + public override string ToString() + { + return ToShortString(); + } } diff --git a/PluginManager/PluginManager.csproj b/PluginManager/PluginManager.csproj index 35db0a2..6acb4eb 100644 --- a/PluginManager/PluginManager.csproj +++ b/PluginManager/PluginManager.csproj @@ -9,22 +9,19 @@ false - + - - - + + + - + ..\..\..\.nuget\packages\spectre.console\0.47.0\lib\net7.0\Spectre.Console.dll - - - \ No newline at end of file diff --git a/PluginManager/Updater/Application/AppUpdater.cs b/PluginManager/Updater/Application/AppUpdater.cs new file mode 100644 index 0000000..877fc50 --- /dev/null +++ b/PluginManager/Updater/Application/AppUpdater.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +using PluginManager.Interfaces.Updater; + +namespace PluginManager.Updater.Application +{ + public class AppUpdater + { + private static readonly string _DefaultUpdateUrl = "https://github.com/andreitdr/SethDiscordBot/releases/latest"; + + private async Task GetOnlineVersion() + { + HttpClient client = new HttpClient(); + var response = await client.GetAsync("https://github.com/andreitdr/SethDiscordBot/releases/latest"); + + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + var version = Regex.Match(content, @".+?v(\d+\.\d+\.\d+.\d+).+?").Groups[1].Value; + + return new AppVersion(version); + } + + return AppVersion.CurrentAppVersion; + } + + public async Task CheckForUpdates() + { + var latestVersion = await GetOnlineVersion(); + if(latestVersion.IsNewerThan(AppVersion.CurrentAppVersion)) + { + return new Update(AppVersion.CurrentAppVersion, latestVersion, _DefaultUpdateUrl, await GetUpdateNotes()); + } + + return Update.None; + } + + private async Task GetUpdateNotes() + { + HttpClient client = new HttpClient(); + var response = await client.GetAsync("https://github.com/andreitdr/SethDiscordBot/releases/latest"); + + if (!response.IsSuccessStatusCode) + { + return string.Empty; + } + + var content = await response.Content.ReadAsStringAsync(); + var markdownStart = content.IndexOf("
", markdownStart) + 1; // Move past the opening tag + var markdownEnd = content.IndexOf("
", 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(); + } + private string ApplyMarkdownFormatting(string markdown) + { + // Apply markdown formatting + markdown = markdown.Replace("**", "**"); // Bold + markdown = markdown.Replace("*", "*"); // Italic + markdown = markdown.Replace("`", "`"); // Inline code + markdown = markdown.Replace("```", "```"); // Code block + markdown = markdown.Replace(">", ">"); // Greater than symbol + markdown = markdown.Replace("<", "<"); // Less than symbol + markdown = markdown.Replace("&", "&"); // Ampersand + markdown = markdown.Replace(""", "\""); // Double quote + markdown = markdown.Replace("'", "'"); // Single quote + markdown = markdown.Replace(" - ", "\n- "); // Convert bullet points to markdown list items + + return markdown; + } + + } +} diff --git a/PluginManager/Updater/Application/Update.cs b/PluginManager/Updater/Application/Update.cs new file mode 100644 index 0000000..a1770f6 --- /dev/null +++ b/PluginManager/Updater/Application/Update.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Discord.Commands; + +using PluginManager.Interfaces.Updater; + +namespace PluginManager.Updater.Application +{ + public class Update + { + public readonly static Update None = new Update(AppVersion.CurrentAppVersion, AppVersion.CurrentAppVersion, string.Empty, string.Empty); + + public AppVersion UpdateVersion { get; private set; } + public AppVersion CurrentVersion { 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) + { + UpdateVersion = updateVersion; + CurrentVersion = currentVersion; + UpdateUrl = updateUrl; + UpdateNotes = updateNotes; + } + } +}