Improved web ui

This commit is contained in:
2024-10-23 20:06:36 +03:00
parent c2dc01cbbb
commit cfcfecd4bc
19 changed files with 271 additions and 197 deletions

View File

@@ -6,7 +6,6 @@ using System.Reflection;
namespace DiscordBot;
public static class Entry
{
/// <summary>
@@ -46,7 +45,7 @@ public static class Entry
\___ \ / _ \ __| '_ \ | | | | / __|/ __/ _ \| '__/ _` | | _ < / _ \| __|
____) | __/ |_| | | | | |__| | \__ \ (_| (_) | | | (_| | | |_) | (_) | |_
|_____/ \___|\__|_| |_| |_____/|_|___/\___\___/|_| \__,_| |____/ \___/ \__|
(Console Application)
";
public static void Main(string[] args)

View File

@@ -65,7 +65,7 @@ public class Program
var token = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("token");
var prefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("prefix");
DiscordBotApplication discordApp = new (token, prefix);
var discordApp = new DiscordBotApplication(token, prefix);
await discordApp.StartAsync();
}
catch (Exception ex)

View File

@@ -12,7 +12,7 @@ public class SqlDatabase
private readonly SQLiteConnection _Connection;
/// <summary>
/// Initialize a SQL connection by specifing its private path
/// Initialize a SQL connection by specifying its private path
/// </summary>
/// <param name="fileName">The path to the database (it is starting from ./Data/Resources/)</param>
public SqlDatabase(string fileName)
@@ -484,7 +484,6 @@ public class SqlDatabase
}
/// <summary>
/// Read data from the result table and return the first row
/// </summary>
@@ -542,7 +541,7 @@ public class SqlDatabase
/// <param name="name">The name of the parameter</param>
/// <param name="value">The value of the parameter</param>
/// <returns>The SQLiteParameter that has the name, value and DBType set according to your inputs</returns>
private SQLiteParameter? CreateParameter(string name, object value)
private static SQLiteParameter? CreateParameter(string name, object value)
{
var parameter = new SQLiteParameter(name);
parameter.Value = value;
@@ -599,7 +598,7 @@ public class SqlDatabase
/// </summary>
/// <param name="parameterValues">The parameter raw inputs. The Key is name and the Value is the value of the parameter</param>
/// <returns>The SQLiteParameter that has the name, value and DBType set according to your inputs</returns>
private SQLiteParameter? CreateParameter(KeyValuePair<string, object> parameterValues)
private static SQLiteParameter? CreateParameter(KeyValuePair<string, object> parameterValues)
{
return CreateParameter(parameterValues.Key, parameterValues.Value);
}

View File

@@ -9,7 +9,7 @@
<link rel="stylesheet" href="app.css"/>
<link rel="stylesheet" href="DiscordBotWebUI.styles.css"/>
<link rel="icon" type="image/png" href="favicon.png"/>
<RadzenTheme Theme="material-dark" @rendermode="InteractiveServer" />
<RadzenTheme Theme="material" @rendermode="InteractiveServer" />
<HeadOutlet/>
</head>

View File

@@ -0,0 +1,8 @@
@code {
[Parameter] public Action CompleteSetup { get; set; }
}
<RadzenHeading Size="H4">Final Setup</RadzenHeading>
<p>Your bot is almost ready! Click 'Finish' to complete the setup.</p>
<RadzenButton Text="Finish" Click="CompleteSetup" Style="margin-top: 20px;" />

View File

@@ -1,16 +0,0 @@
@using DiscordBotWebUI.Types
<Marketplace ListedItems="Items"/>
@code {
private List<MarketItem> Items = new List<MarketItem>();
protected override void OnInitialized()
{
base.OnInitialized();
Items.Clear();
// Load items
}
}

View File

@@ -0,0 +1,48 @@
@using DiscordBotCore
@using DiscordBotCore.Modules
@using DiscordBotCore.Others.Exceptions
@using DiscordBotWebUI.Types
@code {
[Parameter] public Action NextStep { get; set; }
[Parameter] public ModuleRequirement ModuleRequirementReference { get; set; }
private List<MarketItem> MarketItems = new List<MarketItem>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
foreach(var requirement in ModuleRequirementReference.RequiredModulesWithTypes)
{
var modulesWithType = await Application.CurrentApplication.ModuleManager.ServerGetAllModules(requirement);
AppendToList(modulesWithType);
}
foreach (var moduleName in ModuleRequirementReference.RequiredModulesWithNames)
{
var module = await Application.CurrentApplication.ModuleManager.ServerGetModuleWithName(moduleName);
MarketItem item = new MarketItem(module.ModuleName, module.ModuleAuthor, module.ModuleDescription, ItemType.Module);
MarketItems.Add(item);
}
}
private void AppendToList(List<ModuleOnlineData> listOfModules)
{
foreach (var module in listOfModules)
{
MarketItem item = new MarketItem(module.ModuleName, module.ModuleAuthor, module.ModuleDescription, ItemType.Module);
MarketItems.Add(item);
}
}
}
<RadzenHeading Size="H4">Download Dependencies</RadzenHeading>
<p>The bot needs to download certain files to function properly.</p>
@if (MarketItems.Any())
{
<Marketplace ListedItems="MarketItems"/>
}
<RadzenButton Click="NextStep" Text="Next"/>

View File

@@ -0,0 +1,60 @@
@using DiscordBotCore
@code {
[Parameter] public Action NextStep { get; set; }
private string BotToken { get; set; }
private string BotPrefix { get; set; }
private List<ulong> BotServers { get; set; }
private string BotServersString { get; set; }
private async void DoNextStep()
{
if(string.IsNullOrEmpty(BotToken) || string.IsNullOrEmpty(BotPrefix) || string.IsNullOrEmpty(BotServersString))
{
return;
}
if(BotServersString.Contains(","))
{
BotServersString = BotServersString.Replace(",", ";");
}
var stringList = BotServersString.Split(';');
BotServers = new List<ulong>();
foreach(var serverId in stringList)
{
if(ulong.TryParse(serverId, out ulong id))
{
BotServers.Add(id);
}
}
if (!BotServers.Any())
{
return;
}
Application.CurrentApplication.ApplicationEnvironmentVariables.Add("token", BotToken);
Application.CurrentApplication.ApplicationEnvironmentVariables.Add("prefix", BotPrefix);
Application.CurrentApplication.ApplicationEnvironmentVariables.Add("ServerID", BotServers);
await Application.CurrentApplication.ApplicationEnvironmentVariables.SaveToFile();
NextStep.Invoke();
}
}
<RadzenHeading Size="H4">Basic Configuration</RadzenHeading>
<p>Please provide some basic settings to get started.</p>
<RadzenLabel Text="Token" />
<RadzenTextBox @bind-Value="BotToken" Placeholder="Enter bot token here" Style="width: 100%;" />
<RadzenLabel Text="Prefix" Style="margin-top: 10px;" />
<RadzenTextBox @bind-Value="BotPrefix" Placeholder="Enter the prefix here" Style="width: 100%;" />
<RadzenLabel Text="Server Ids separated by ;" Style="margin-top: 20px;" />
<RadzenTextBox @bind-Value="BotServersString" Placeholder="Enter Server Ids here. Separated by commas (;)" Style="width: 100%;" />
<RadzenButton Text="Next" Click="DoNextStep" Style="margin-top: 30px;" />

View File

@@ -1,17 +1,7 @@
<div style="display: flex; justify-content: center; align-items: center; height: 100vh;">
<RadzenCard Style="position: relative; background-color: #2d3748; color: #fff; max-width: 450px; padding: 30px; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.3); border-radius: 12px;">
<RadzenHeading Size="H2" Text=@_Title Style="text-align: center; color: #e2e8f0;" />
<RadzenText Text=@_Text Style="text-align: center; color: #a0aec0;" />
</RadzenCard>
</div>
@code {
private static readonly string _Title = "Welcome to Seth Discord Bot setup page";
private static readonly string _Text =
@"
Seth Discord Bot is a small yet powerful Discord Bot that allows you to integrate custom commands and events into your Discord server.
But let's start with the configuration first. Please click the arrow to the right to begin ...
";
@code {
[Parameter] public Action NextStep { get; set; }
}
<RadzenHeading Size="H4">Welcome to the Bot Setup Wizard</RadzenHeading>
<p>This setup wizard will guide you through the process of configuring your bot and downloading the necessary dependencies.</p>
<RadzenButton Text="Start Setup" Click="NextStep" Style="margin-top: 20px;" />

View File

@@ -1,72 +0,0 @@
@page "/"
@using DiscordBotCore.Others.Exceptions
@using DiscordBotWebUI.Components.Pages.Setup
@using DiscordBotWebUI.DiscordBot
@inject DialogService DialogService
<RadzenButton Text="Start" Click="Initialize"/>
<RadzenTextArea Placeholder="Logs..." Value="@_TextValue" Style="width: 100%; height: 80%"/>
@code {
private string? _TextValue;
private DiscordBotStartup _DiscordBotStartup = null!;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
// Only run after the first render
if(!firstRender)
{
return;
}
_DiscordBotStartup = new DiscordBotStartup {RequirementsSolver = FixModules};
await _DiscordBotStartup.CreateApplication();
}
private async Task FixModules(ModuleRequirement requirements)
{
if(!requirements.RequireAny)
{
return;
}
await DialogService.OpenAsync<InstallRequiredModules>("Some required modules are not installed. Please install before using the bot ...", new Dictionary<string, object>()
{
{"Requirements", requirements}
}, new DialogOptions() { Width = "75%", Height = "75%" });
}
private async void Initialize()
{
_DiscordBotStartup.Log = async (str, type) => {
_TextValue += $"[{type}] {str} \n";
await InvokeAsync(StateHasChanged);
Console.WriteLine(str);
};
if (_DiscordBotStartup.LoadComponents())
{
await _DiscordBotStartup.PrepareBot();
await _DiscordBotStartup.RefreshPlugins(false);
return;
}
while (await DialogService.OpenAsync<Settings>("Please complete this setup before starting the bot") == false)
{
Console.WriteLine("Failed to complete the setup. Invalid data acquired ...");
}
await _DiscordBotStartup.PrepareBot();
await _DiscordBotStartup.RefreshPlugins(false);
}
}

View File

@@ -1,45 +0,0 @@
@using DiscordBotCore
@using DiscordBotCore.Modules
@using DiscordBotCore.Others.Exceptions
@using DiscordBotWebUI.Components.Items
@using DiscordBotWebUI.Types
@if(MarketItems.Count > 0)
{
<Marketplace ListedItems="MarketItems"/>
}
@code {
[Parameter]
public ModuleRequirement Requirements { get; set; }
private List<MarketItem> MarketItems = new List<MarketItem>();
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
foreach(var requirement in Requirements.RequiredModulesWithTypes)
{
var modulesWithType = await Application.CurrentApplication.ModuleManager.ServerGetAllModules(requirement);
AppendToList(modulesWithType);
}
foreach (var moduleName in Requirements.RequiredModulesWithNames)
{
var module = await Application.CurrentApplication.ModuleManager.ServerGetModuleWithName(moduleName);
MarketItem item = new MarketItem(module.ModuleName, module.ModuleAuthor, module.ModuleDescription, ItemType.Module);
MarketItems.Add(item);
}
}
private void AppendToList(List<ModuleOnlineData> listOfModules)
{
foreach (var module in listOfModules)
{
MarketItem item = new MarketItem(module.ModuleName, module.ModuleAuthor, module.ModuleDescription, ItemType.Module);
MarketItems.Add(item);
}
}
}

View File

@@ -0,0 +1,51 @@
@page "/setup-wizard"
@inject DialogService DialogService
@using DiscordBotCore
@using DiscordBotCore.Others.Exceptions
@using DiscordBotWebUI.Components.Items.Setup
<RadzenCard Style="max-width: 600px; margin: auto; margin-top: 50px; padding: 20px;">
<RadzenSteps @bind-Value="currentStep" ShowStepsButtons="false">
<RadzenStepsItem Text="Welcome" />
<RadzenStepsItem Text="Basic Configuration" />
<RadzenStepsItem Text="Download Dependencies" />
<RadzenStepsItem Text="Final Setup" />
</RadzenSteps>
<div>
@if (currentStep == 0)
{
<WelcomeComponent NextStep="NextStep" />
}
else if (currentStep == 1)
{
<StartupConfigurationComponent NextStep="NextStep" />
}
else if (currentStep == 2)
{
<ModuleSetupComponent ModuleRequirementReference="RequirementsToDownload" NextStep="NextStep" />
}
else if (currentStep == 3)
{
<FinishSetupComponent CompleteSetup="FinishSetup" />
}
</div>
</RadzenCard>
@code {
[Parameter] public ModuleRequirement RequirementsToDownload { get; set; }
private int currentStep = 0;
private void NextStep()
{
currentStep++;
StateHasChanged();
}
private void FinishSetup()
{
DialogService.Close(true);
}
}

View File

@@ -1,19 +0,0 @@
@page "/welcome"
@using DiscordBotWebUI.Components.Items.Setup
@inject NavigationManager Navigation
<WelcomeComponent/>
<RadzenButton
Icon="arrow_forward"
ButtonStyle="ButtonStyle.Dark"
Style="position: absolute; right: -50px; top: 50%; transform: translateY(-50%); background-color: #2d3748; border: none; color: #63b3ed;"
Click="GoToNextPage" />
@code {
// Method to navigate to a new page
private void GoToNextPage()
{
Navigation.NavigateTo("/settings");
}
}

View File

@@ -0,0 +1,64 @@
@page "/"
@using DiscordBotCore.Others
@using DiscordBotCore.Others.Exceptions
@using DiscordBotWebUI.Components.Pages.Setup
@using DiscordBotWebUI.DiscordBot
@inject DialogService DialogService
<RadzenButton Text="Start" Click="Initialize"/>
<RadzenTextArea Placeholder="Logs..." Value="@_TextValue" Style="width: 100%; height: 80%"/>
@code {
private string? _TextValue;
private async Task Solver(ModuleRequirement moduleRequirement)
{
while (await DialogService.OpenAsync<SetupWizard>("Setup Wizard", new Dictionary<string, object> {{"RequirementsToDownload", moduleRequirement}}) != true)
{
Console.WriteLine("Failed to complete the setup. Invalid data acquired ...");
}
}
private async Task Initialize()
{
Action<string, LogType> logging = async (str, type) => {
_TextValue += $"[{type}] {str} \n";
await InvokeAsync(StateHasChanged);
};
var discordApplication = new DiscordApplication(logging, Solver);
await discordApplication.Start();
}
// private async void Initialize()
// {
// _DiscordBotStartup = new DiscordBotStartup { RequirementsSolver = default! };
// await _DiscordBotStartup.CreateApplication();
//
// _DiscordBotStartup.Log = async (str, type) => {
// _TextValue += $"[{type}] {str} \n";
// await InvokeAsync(StateHasChanged);
//
// Console.WriteLine(str);
// };
//
// if (_DiscordBotStartup.LoadComponents())
// {
// await _DiscordBotStartup.PrepareBot();
// await _DiscordBotStartup.RefreshPlugins(false);
//
// return;
// }
//
// while (await DialogService.OpenAsync<Settings>("Please complete this setup before starting the bot") == false)
// {
// Console.WriteLine("Failed to complete the setup. Invalid data acquired ...");
// }
//
// await _DiscordBotStartup.PrepareBot();
// await _DiscordBotStartup.RefreshPlugins(false);
// }
}

View File

@@ -6,26 +6,24 @@ using DiscordBotCore.Others.Exceptions;
namespace DiscordBotWebUI.DiscordBot;
public class DiscordBotStartup
public class DiscordApplication
{
public required Func<ModuleRequirement, Task> RequirementsSolver { get; set; }
public bool IsRunning { get; private set; }
private Action<string, LogType> LogWriter { get; set; }
private Func<ModuleRequirement, Task> RequirementsSolver { get; set; }
internal delegate void LogEventHandler(string message, LogType type);
internal LogEventHandler Log;
private void WriteLog(string message, LogType logType)
public DiscordApplication(Action<string, LogType> logWriter, Func<ModuleRequirement, Task> requirementsSolver)
{
Log?.Invoke(message, logType);
this.LogWriter = logWriter;
this.RequirementsSolver = requirementsSolver;
IsRunning = false;
}
public async Task CreateApplication()
private async Task<bool> LoadComponents()
{
await Application.CreateApplication(RequirementsSolver);
}
public bool LoadComponents()
{
Application.Logger.SetOutFunction(WriteLog);
await Application.CreateApplication(RequirementsSolver); // This is a placeholder for the RequirementsSolver delegate
Application.Logger.SetOutFunction(LogWriter);
return Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("ServerID") &&
Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("token") &&
@@ -33,13 +31,23 @@ public class DiscordBotStartup
}
public async Task PrepareBot()
public async Task Start()
{
if (!await LoadComponents())
{
return;
}
var token = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("token");
var prefix = Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("prefix");
DiscordBotApplication app = new DiscordBotApplication(token, prefix);
await app.StartAsync();
var coreApplication = new DiscordBotApplication(token, prefix);
await coreApplication.StartAsync();
await RefreshPlugins(false);
IsRunning = true;
}
public async Task RefreshPlugins(bool quiet)
@@ -79,5 +87,4 @@ public class DiscordBotStartup
{
await Application.CurrentApplication.InternalActionManager.Initialize();
}
}