Improved UI experience for the Main page
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using Discord.WebSocket;
|
||||
using DiscordBotCore.PluginCore;
|
||||
using DiscordBotCore.PluginCore.Interfaces;
|
||||
|
||||
@@ -9,4 +10,6 @@ public interface IPluginLoader
|
||||
List<IDbEvent> Events { get; }
|
||||
List<IDbSlashCommand> SlashCommands { get; }
|
||||
Task LoadPlugins();
|
||||
|
||||
void SetClient(DiscordSocketClient client);
|
||||
}
|
||||
@@ -13,27 +13,19 @@ namespace DiscordBotCore.PluginManagement.Loading;
|
||||
|
||||
public sealed class PluginLoader : IPluginLoader
|
||||
{
|
||||
private readonly DiscordSocketClient _DiscordClient;
|
||||
private readonly IPluginManager _PluginManager;
|
||||
private readonly ILogger _Logger;
|
||||
private readonly IConfiguration _Configuration;
|
||||
|
||||
public delegate void CommandLoaded(IDbCommand eCommand);
|
||||
public delegate void EventLoaded(IDbEvent eEvent);
|
||||
public delegate void SlashCommandLoaded(IDbSlashCommand eSlashCommand);
|
||||
|
||||
public CommandLoaded? OnCommandLoaded;
|
||||
public EventLoaded? OnEventLoaded;
|
||||
public SlashCommandLoaded? OnSlashCommandLoaded;
|
||||
|
||||
public List<IDbCommand> Commands { get; private set; } = new List<IDbCommand>();
|
||||
public List<IDbEvent> Events { get; private set; } = new List<IDbEvent>();
|
||||
public List<IDbSlashCommand> SlashCommands { get; private set; } = new List<IDbSlashCommand>();
|
||||
|
||||
public PluginLoader(IPluginManager pluginManager, ILogger logger, IConfiguration configuration, DiscordSocketClient discordSocketDiscordClient)
|
||||
private DiscordSocketClient? _discordClient;
|
||||
|
||||
public PluginLoader(IPluginManager pluginManager, ILogger logger, IConfiguration configuration)
|
||||
{
|
||||
_PluginManager = pluginManager;
|
||||
_DiscordClient = discordSocketDiscordClient;
|
||||
_Logger = logger;
|
||||
_Configuration = configuration;
|
||||
}
|
||||
@@ -54,6 +46,24 @@ public sealed class PluginLoader : IPluginLoader
|
||||
await loader.Load();
|
||||
}
|
||||
|
||||
public void SetClient(DiscordSocketClient client)
|
||||
{
|
||||
if (_discordClient is not null)
|
||||
{
|
||||
_Logger.Log("A client is already set. Please set the client only once.", this, LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.LoginState != LoginState.LoggedIn)
|
||||
{
|
||||
_Logger.Log("Client is not logged in. Retry after the client is logged in", this, LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
_Logger.Log("Client is set to the plugin loader", this);
|
||||
_discordClient = client;
|
||||
}
|
||||
|
||||
private void FileLoadedException(string fileName, Exception exception)
|
||||
{
|
||||
_Logger.LogException(exception, this);
|
||||
@@ -62,7 +72,7 @@ public sealed class PluginLoader : IPluginLoader
|
||||
private void InitializeDbCommand(IDbCommand command)
|
||||
{
|
||||
Commands.Add(command);
|
||||
OnCommandLoaded?.Invoke(command);
|
||||
_Logger.Log("Command loaded: " + command.Command, this);
|
||||
}
|
||||
|
||||
private void InitializeEvent(IDbEvent eEvent)
|
||||
@@ -73,7 +83,7 @@ public sealed class PluginLoader : IPluginLoader
|
||||
}
|
||||
|
||||
Events.Add(eEvent);
|
||||
OnEventLoaded?.Invoke(eEvent);
|
||||
_Logger.Log("Event loaded: " + eEvent, this);
|
||||
}
|
||||
|
||||
private async void InitializeSlashCommand(IDbSlashCommand slashCommand)
|
||||
@@ -83,9 +93,9 @@ public sealed class PluginLoader : IPluginLoader
|
||||
() =>
|
||||
{
|
||||
if (slashCommand.HasInteraction)
|
||||
_DiscordClient.InteractionCreated += interaction => slashCommand.ExecuteInteraction(_Logger, interaction);
|
||||
_discordClient.InteractionCreated += interaction => slashCommand.ExecuteInteraction(_Logger, interaction);
|
||||
SlashCommands.Add(slashCommand);
|
||||
OnSlashCommandLoaded?.Invoke(slashCommand);
|
||||
_Logger.Log("Slash command loaded: " + slashCommand.Name, this);
|
||||
},
|
||||
HandleError
|
||||
);
|
||||
@@ -116,7 +126,7 @@ public sealed class PluginLoader : IPluginLoader
|
||||
}
|
||||
IDbEventExecutingArgument args = new DbEventExecutingArgument(
|
||||
_Logger,
|
||||
_DiscordClient,
|
||||
_discordClient,
|
||||
_Configuration.Get<string>("prefix"),
|
||||
new DirectoryInfo(Path.Combine(_Configuration.Get<string>("ResourcesPath"), dbEvent.Name)));
|
||||
dbEvent.Start(args);
|
||||
@@ -139,7 +149,7 @@ public sealed class PluginLoader : IPluginLoader
|
||||
return Result.Failure(new Exception("dbSlashCommand is null"));
|
||||
}
|
||||
|
||||
if (_DiscordClient.Guilds.Count == 0)
|
||||
if (_discordClient.Guilds.Count == 0)
|
||||
{
|
||||
return Result.Failure(new Exception("No guilds found"));
|
||||
}
|
||||
@@ -166,7 +176,7 @@ public sealed class PluginLoader : IPluginLoader
|
||||
}
|
||||
}
|
||||
|
||||
await _DiscordClient.CreateGlobalApplicationCommandAsync(builder.Build());
|
||||
await _discordClient.CreateGlobalApplicationCommandAsync(builder.Build());
|
||||
|
||||
return Result.Success();
|
||||
}
|
||||
@@ -178,7 +188,7 @@ public sealed class PluginLoader : IPluginLoader
|
||||
|
||||
private async Task<bool> EnableSlashCommandPerGuild(ulong guildId, SlashCommandBuilder builder)
|
||||
{
|
||||
SocketGuild? guild = _DiscordClient.GetGuild(guildId);
|
||||
SocketGuild? guild = _discordClient.GetGuild(guildId);
|
||||
if (guild is null)
|
||||
{
|
||||
_Logger.Log("Failed to get guild with ID " + guildId, typeof(PluginLoader), LogType.Error);
|
||||
|
||||
@@ -20,7 +20,7 @@ public class DiscordBotApplication : IDiscordBotApplication
|
||||
private readonly IConfiguration _Configuration;
|
||||
private readonly IPluginLoader _PluginLoader;
|
||||
|
||||
private bool IsReady { get; set; }
|
||||
public bool IsReady { get; private set; }
|
||||
|
||||
public DiscordSocketClient Client { get; private set; }
|
||||
|
||||
@@ -34,6 +34,28 @@ public class DiscordBotApplication : IDiscordBotApplication
|
||||
this._PluginLoader = pluginLoader;
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (!IsReady)
|
||||
{
|
||||
_Logger.Log("Can not stop the bot. It is not yet initialized.", this, LogType.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
await Client.LogoutAsync();
|
||||
await Client.StopAsync();
|
||||
|
||||
Client.Log -= Log;
|
||||
Client.LoggedIn -= LoggedIn;
|
||||
Client.Ready -= Ready;
|
||||
Client.Disconnected -= Client_Disconnected;
|
||||
|
||||
await Client.DisposeAsync();
|
||||
|
||||
IsReady = false;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The start method for the bot. This method is used to load the bot
|
||||
/// </summary>
|
||||
@@ -91,6 +113,7 @@ public class DiscordBotApplication : IDiscordBotApplication
|
||||
private Task LoggedIn()
|
||||
{
|
||||
_Logger.Log("Successfully Logged In", this);
|
||||
_PluginLoader.SetClient(Client);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,16 @@ namespace DiscordBotCore.Bot;
|
||||
|
||||
public interface IDiscordBotApplication
|
||||
{
|
||||
public bool IsReady { get; }
|
||||
public DiscordSocketClient Client { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The start method for the bot. This method is used to load the bot
|
||||
/// </summary>
|
||||
Task StartAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Stops the bot and cleans up resources.
|
||||
/// </summary>
|
||||
Task StopAsync();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using DiscordBotCore.Bot;
|
||||
using DiscordBotCore.PluginManagement.Loading;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WebUI.Models;
|
||||
using ILogger = DiscordBotCore.Logging.ILogger;
|
||||
|
||||
namespace WebUI.Controllers;
|
||||
@@ -8,25 +8,64 @@ namespace WebUI.Controllers;
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
public HomeController(ILogger logger)
|
||||
private readonly IDiscordBotApplication _discordBotApplication;
|
||||
private readonly IPluginLoader _pluginLoader;
|
||||
|
||||
public HomeController(ILogger logger, IDiscordBotApplication discordBotApplication, IPluginLoader pluginLoader)
|
||||
{
|
||||
_logger = logger;
|
||||
_discordBotApplication = discordBotApplication;
|
||||
_pluginLoader = pluginLoader;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Index()
|
||||
{
|
||||
_logger.Log("Index page loaded", this);
|
||||
ViewBag.IsRunning = _discordBotApplication.IsReady;
|
||||
return View();
|
||||
}
|
||||
|
||||
public IActionResult Privacy()
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> StartApplication()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
if (_discordBotApplication.IsReady)
|
||||
{
|
||||
_logger.Log("Application already started", this);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
|
||||
public IActionResult Error()
|
||||
await _discordBotApplication.StartAsync();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> StopApplication()
|
||||
{
|
||||
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
|
||||
if (!_discordBotApplication.IsReady)
|
||||
{
|
||||
_logger.Log("Application already stopped", this);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
await _discordBotApplication.StopAsync();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public JsonResult GetLogs()
|
||||
{
|
||||
var logText = _logger.GetLogsHistory();
|
||||
var logs = logText.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
|
||||
return Json(logs);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> LoadPlugins()
|
||||
{
|
||||
_logger.Log("Loading plugins", this);
|
||||
await _pluginLoader.LoadPlugins();
|
||||
_logger.Log("Plugins loaded", this);
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
}
|
||||
@@ -142,6 +142,15 @@ builder.Services.AddSingleton<IPluginManager>(sp =>
|
||||
IPluginManager pluginManager = new PluginManager(pluginRepository, logger, configuration);
|
||||
return pluginManager;
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IPluginLoader>(sp =>
|
||||
{
|
||||
IPluginManager pluginManager = sp.GetRequiredService<IPluginManager>();
|
||||
ILogger logger = sp.GetRequiredService<ILogger>();
|
||||
IConfiguration configuration = sp.GetRequiredService<IConfiguration>();
|
||||
return new PluginLoader(pluginManager, logger, configuration);
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IDiscordBotApplication>(sp =>
|
||||
{
|
||||
ILogger logger = sp.GetRequiredService<ILogger>();
|
||||
@@ -150,14 +159,7 @@ builder.Services.AddSingleton<IDiscordBotApplication>(sp =>
|
||||
return new DiscordBotApplication(logger, configuration, pluginLoader);
|
||||
});
|
||||
|
||||
builder.Services.AddSingleton<IPluginLoader>(sp =>
|
||||
{
|
||||
IPluginManager pluginManager = sp.GetRequiredService<IPluginManager>();
|
||||
ILogger logger = sp.GetRequiredService<ILogger>();
|
||||
IConfiguration configuration = sp.GetRequiredService<IConfiguration>();
|
||||
IDiscordBotApplication discordBotApplication = sp.GetRequiredService<IDiscordBotApplication>();
|
||||
return new PluginLoader(pluginManager, logger, configuration, discordBotApplication.Client);
|
||||
});
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -1,8 +1,59 @@
|
||||
@{
|
||||
ViewData["Title"] = "Home Page";
|
||||
var isRunning = ViewBag.IsRunning ?? false;
|
||||
ViewBag.Title = "Console Log Viewer";
|
||||
}
|
||||
|
||||
<div class="text-center">
|
||||
<h1 class="display-4">Welcome</h1>
|
||||
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
|
||||
</div>
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h4>Console Log</h4>
|
||||
<div id="consoleLog" class="border p-3 bg-dark text-white" style="height: 400px; overflow-y: scroll; font-family: monospace;"></div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<h4>Controls</h4>
|
||||
<form method="post" asp-action="StartApplication">
|
||||
<button type="submit" class="btn btn-success mb-2">Start Application</button>
|
||||
</form>
|
||||
@if (isRunning)
|
||||
{
|
||||
<form method="post" asp-action="StopApplication">
|
||||
<button type="submit" class="btn btn-danger mb-2">Stop Application</button>
|
||||
</form>
|
||||
<form method="post" asp-action="LoadPlugins">
|
||||
<button type="submit" class="btn btn-info mb-2">Load Plugins</button>
|
||||
</form>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
<script>
|
||||
let lastLogCount = 0;
|
||||
|
||||
function fetchLogs() {
|
||||
fetch("/Home/GetLogs")
|
||||
.then(res => res.json())
|
||||
.then(logs => {
|
||||
const logDiv = document.getElementById("consoleLog");
|
||||
if (logs.length !== lastLogCount) {
|
||||
logDiv.innerHTML = logs.map(line => formatLog(line)).join('');
|
||||
logDiv.scrollTop = logDiv.scrollHeight;
|
||||
lastLogCount = logs.length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function formatLog(line) {
|
||||
if (line.includes("[Error]")) return `<div style="color: #ff4d4d;">${line}</div>`;
|
||||
if (line.includes("[Warning]")) return `<div style="color: #ffa500;">${line}</div>`;
|
||||
if (line.includes("[Debug]")) return `<div style="color: #88f;">${line}</div>`;
|
||||
return `<div>${line}</div>`;
|
||||
}
|
||||
|
||||
setInterval(fetchLogs, 2000);
|
||||
fetchLogs();
|
||||
</script>
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@{
|
||||
ViewData["Title"] = "Privacy Policy";
|
||||
}
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
|
||||
<p>Use this page to detail your site's privacy policy.</p>
|
||||
Reference in New Issue
Block a user