Redesigned the DiscordBotCore by splitting it into multiple projects. Created a WebUI and preparing to remove the DiscordBot application

This commit is contained in:
2025-04-04 22:07:30 +03:00
parent 62ba5ec63d
commit a4afb28f36
2290 changed files with 76694 additions and 17052 deletions

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,112 +0,0 @@
using System.Runtime.InteropServices;
using DiscordBotCore;
namespace CppWrapper.LibraryManagement
{
public sealed class ExternLibrary
{
public string LibraryPath { get; init; }
public IntPtr LibraryHandle { get; private set; }
public ExternLibrary(string libraryPath)
{
LibraryPath = libraryPath;
LibraryHandle = IntPtr.Zero;
}
public void InitializeLibrary()
{
if(LibraryHandle != IntPtr.Zero)
{
return;
}
Application.CurrentApplication.Logger.Log($"Loading library {LibraryPath}");
if(!NativeLibrary.TryLoad(LibraryPath, out IntPtr hModule))
{
throw new DllNotFoundException($"Unable to load library {LibraryPath}");
}
Application.CurrentApplication.Logger.Log($"Library {LibraryPath} loaded successfully [{hModule}]");
LibraryHandle = hModule;
}
public void FreeLibrary()
{
if(LibraryHandle == IntPtr.Zero)
{
return;
}
NativeLibrary.Free(LibraryHandle);
LibraryHandle = IntPtr.Zero;
Application.CurrentApplication.Logger.Log($"Library {LibraryPath} freed successfully");
}
private IntPtr GetFunctionPointer(string functionName)
{
if(LibraryHandle == IntPtr.Zero)
{
throw new InvalidOperationException("Library is not loaded");
}
if(!NativeLibrary.TryGetExport(LibraryHandle, functionName, out IntPtr functionPointer))
{
throw new EntryPointNotFoundException($"Unable to find function {functionName}");
}
return functionPointer;
}
public T GetDelegateForFunctionPointer<T>(string methodName) where T : Delegate
{
IntPtr functionPointer = GetFunctionPointer(methodName);
Application.CurrentApplication.Logger.Log($"Function pointer for {methodName} obtained successfully [address: {functionPointer}]");
T result = (T)Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(T));
Application.CurrentApplication.Logger.Log($"Delegate for {methodName} created successfully");
return result;
}
private IntPtr GetFunctionPointerForDelegate<T>(T functionDelegate) where T : Delegate
{
IntPtr functionPointer = Marshal.GetFunctionPointerForDelegate(functionDelegate);
Application.CurrentApplication.Logger.Log($"Function pointer for delegate {functionDelegate.Method.Name} obtained successfully [address: {functionPointer}]");
return functionPointer;
}
/// <summary>
/// Tells the extern setter function to point its function to this C# function instead.
/// This function takes the name of the extern setter function and the C# function to be executed.
/// <para><b>How it works:</b></para>
/// Find the external setter method by its name. It should take one parameter, which is the pointer to the function to be executed.
/// Take the delegate function that should be executed and get its function pointer.
/// Call the external setter with the new function memory address. This should replace the old C++ function with the new C# function.
/// </summary>
/// <param name="setterExternFunctionName">The setter function name</param>
/// <param name="executableFunction">The function that the C++ setter will make its internal function to point to</param>
/// <typeparam name="ExecuteDelegate">A delegate that reflects the executable function structure</typeparam>
/// <typeparam name="SetDelegate">The Setter delegate </typeparam>
/// <returns>A response if it exists as an object</returns>
public object? SetExternFunctionSetterPointerToCustomDelegate<SetDelegate, ExecuteDelegate>(string setterExternFunctionName, ExecuteDelegate executableFunction) where ExecuteDelegate : Delegate where SetDelegate : Delegate
{
SetDelegate setterDelegate = GetDelegateForFunctionPointer<SetDelegate>(setterExternFunctionName);
IntPtr executableFunctionPtr = GetFunctionPointerForDelegate(executableFunction);
var result = setterDelegate.DynamicInvoke(executableFunctionPtr);
Application.CurrentApplication.Logger.Log($"Function {setterExternFunctionName} bound to local action successfully");
return result;
}
}
}

View File

@@ -1,34 +0,0 @@
using System.Runtime.InteropServices;
namespace CppWrapper.Objects
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ApplicationStruct
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public ulong[] ServerIds;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
public string Prefix;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 71)]
public string Token;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct ComplexObject
{
public int Integer;
public double DoubleValue;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string strValue;
public ComplexObject(int integer, double doubleValue, string strValue)
{
Integer = integer;
DoubleValue = doubleValue;
this.strValue = strValue;
}
}
}

View File

@@ -1,17 +0,0 @@
using DiscordBotCore;
namespace CppWrapper.Objects
{
public static class ObjectConvertor
{
public static ApplicationStruct ToApplicationStruct(this Application application)
{
return new ApplicationStruct
{
Token = application.ApplicationEnvironmentVariables.Get<string>("token") ?? throw new Exception("Token not found"),
Prefix = application.ApplicationEnvironmentVariables.Get<string>("prefix") ?? throw new Exception("Prefix not found"),
ServerIds = application.ServerIDs.ToArray()
};
}
}
}

View File

@@ -1,22 +0,0 @@
using System.Runtime.InteropServices;
using CppWrapper.Objects;
namespace DiscordBotUI
{
public abstract class Delegates
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ProcessApplicationData(ref ApplicationStruct appData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ProcessComplexObject(ref ComplexObject complexObject);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CsharpFunctionDelegate();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void SetCsharpFunctionPointerDelegate(IntPtr funcPtr);
}
}

View File

@@ -1,13 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
<ProjectReference Include="..\CppWrapper\CppWrapper.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,55 +0,0 @@
using DiscordBotCore.Interfaces;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Actions;
using CppWrapper.Objects;
using CppWrapper.LibraryManagement;
using DiscordBotCore;
using CppWrapper;
namespace DiscordBotUI;
public class Entry : ICommandAction
{
public string ActionName => "cppui";
public string? Description => "A C++ linker to the C++ UI for the bot";
public string? Usage => "cppui";
public IEnumerable<InternalActionOption> ListOfOptions => [];
public InternalActionRunType RunType => InternalActionRunType.OnStartupAndCall;
public bool RequireOtherThread => false;
public Task Execute(string[]? args)
{
try{
string appUiComponent = "./Data/Test/libtestlib.dll";
ExternLibrary externalLibrary = new ExternLibrary(appUiComponent);
externalLibrary.InitializeLibrary();
externalLibrary.SetExternFunctionSetterPointerToCustomDelegate<Delegates.SetCsharpFunctionPointerDelegate, Delegates.CsharpFunctionDelegate>("setCSharpFunctionPointer", () =>
{
Console.WriteLine("Hello from C#. This code is called from the C# function");
});
Delegates.ProcessComplexObject processObj = externalLibrary.GetDelegateForFunctionPointer<Delegates.ProcessComplexObject>("ProcessComplexObject");
ComplexObject complexObject = new ComplexObject(10, 10.5, "Hello from C#");
processObj(ref complexObject);
Console.WriteLine($"Integer: {complexObject.Integer}");
Console.WriteLine($"Double: {complexObject.DoubleValue}");
Console.WriteLine($"String: {complexObject.strValue}");
externalLibrary.FreeLibrary();
} catch (Exception dllException) {
Application.CurrentApplication.Logger.LogException(dllException, this);
}
return Task.CompletedTask;
}
}

View File

@@ -1,7 +1,8 @@
using Discord;
using DiscordBotCore;
using DiscordBotCore.Interfaces;
using DiscordBotCore.Others;
using DiscordBotCore.Logging;
using DiscordBotCore.PluginCore.Helpers;
using DiscordBotCore.PluginCore.Helpers.Execution.DbCommand;
using DiscordBotCore.PluginCore.Interfaces;
namespace LevelingSystem;
@@ -17,11 +18,11 @@ internal class LevelCommand: IDbCommand
public bool RequireAdmin => false;
public async void ExecuteServer(DbCommandExecutingArguments args)
public async Task ExecuteServer(IDbCommandExecutingArgument args)
{
if(Variables.Database is null)
{
Application.CurrentApplication.Logger.Log("Database is not initialized", this, LogType.Warning);
args.Logger.Log("Database is not initialized", this);
return;
}

View File

@@ -1,8 +1,11 @@
using Discord.WebSocket;
using DiscordBotCore;
using DiscordBotCore.Database;
using DiscordBotCore.Interfaces;
using System.Net.Mime;
using Discord.WebSocket;
using DiscordBotCore.Database.Sqlite;
using DiscordBotCore.Logging;
using DiscordBotCore.PluginCore;
using DiscordBotCore.PluginCore.Helpers;
using DiscordBotCore.PluginCore.Helpers.Execution.DbEvent;
using DiscordBotCore.PluginCore.Interfaces;
using static LevelingSystem.Variables;
namespace LevelingSystem;
@@ -12,7 +15,7 @@ internal class LevelEvent : IDbEvent
public string Name => "Leveling System Event Handler";
public string Description => "The Leveling System Event Handler";
public async void Start(DiscordSocketClient client)
public async Task Start(IDbEventExecutingArgument args)
{
Directory.CreateDirectory(DataFolder);
@@ -29,10 +32,11 @@ internal class LevelEvent : IDbEvent
MaxExp = 7,
MinExp = 1
};
await DiscordBotCore.Others.JsonManager.SaveToJsonFile(DataFolder + "Settings.txt", GlobalSettings);
await DiscordBotCore.Utilities.JsonManager.SaveToJsonFile(DataFolder + "Settings.txt", GlobalSettings);
}
else
GlobalSettings = await DiscordBotCore.Others.JsonManager.ConvertFromJson<Settings>(DataFolder + "Settings.txt");
GlobalSettings = await DiscordBotCore.Utilities.JsonManager.ConvertFromJson<Settings>(DataFolder + "Settings.txt");
if (!await Database.TableExistsAsync("Levels"))
await Database.CreateTableAsync("Levels", "UserID VARCHAR(128)", "Level INT", "EXP INT");
@@ -40,14 +44,12 @@ internal class LevelEvent : IDbEvent
if (!await Database.TableExistsAsync("Users"))
await Database.CreateTableAsync("Users", "UserID VARCHAR(128)", "UserMention VARCHAR(128)", "Username VARCHAR(128)", "Discriminator VARCHAR(128)");
client.MessageReceived += ClientOnMessageReceived;
args.Client.MessageReceived += (message) => ClientOnMessageReceived(message, args.BotPrefix);
}
private async Task ClientOnMessageReceived(SocketMessage arg)
private async Task ClientOnMessageReceived(SocketMessage arg, string botPrefix)
{
if (arg.Author.IsBot || arg.IsTTS || arg.Content.StartsWith(Application.CurrentApplication.ApplicationEnvironmentVariables.Get<string>("prefix")))
if (arg.Author.IsBot || arg.IsTTS || arg.Content.StartsWith(botPrefix))
return;
if (WaitingList.ContainsKey(arg.Author.Id) && WaitingList[arg.Author.Id] > DateTime.Now.AddSeconds(-GlobalSettings.SecondsToWaitBetweenMessages))

View File

@@ -7,7 +7,10 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
<ProjectReference Include="..\..\DiscordBotCore.Database.Sqlite\DiscordBotCore.Database.Sqlite.csproj" />
<ProjectReference Include="..\..\DiscordBotCore.Logging\DiscordBotCore.Logging.csproj" />
<ProjectReference Include="..\..\DiscordBotCore.PluginCore\DiscordBotCore.PluginCore.csproj" />
<ProjectReference Include="..\..\DiscordBotCore.Utilities\DiscordBotCore.Utilities.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,20 +0,0 @@
using DiscordBotCore.Interfaces;
using DiscordBotCore.Others;
using DiscordBotCore.Others.Actions;
namespace LevelingSystem;
public class ReloadAction: ICommandAction
{
public string ActionName => "LevelingSystemReload";
public string? Description => "Reloads the Leveling System config file";
public string? Usage => "LevelingSystemReload";
public InternalActionRunType RunType => InternalActionRunType.OnCall;
public bool RequireOtherThread => false;
public IEnumerable<InternalActionOption> ListOfOptions => [];
public async Task Execute(string[]? args)
{
Variables.GlobalSettings = await JsonManager.ConvertFromJson<Settings>(Variables.DataFolder + "Settings.txt");
}
}

View File

@@ -1,5 +1,4 @@
using DiscordBotCore;
using DiscordBotCore.Database;
using DiscordBotCore.Database.Sqlite;
namespace LevelingSystem;
@@ -12,8 +11,8 @@ public class Settings
internal static class Variables
{
internal static readonly string DataFolder = Application.GetResourceFullPath("LevelingSystem/");
internal static SqlDatabase? Database;
internal static readonly Dictionary<ulong, DateTime> WaitingList = new();
internal static Settings GlobalSettings = new();
}
internal static readonly string DataFolder = "./Data/Resources/LevelingSystem/";
internal static SqlDatabase? Database;
internal static readonly Dictionary<ulong, DateTime> WaitingList = new();
internal static Settings GlobalSettings = new();
}

View File

@@ -1,9 +1,8 @@
using Discord;
using DiscordBotCore;
using DiscordBotCore.Interfaces;
using DiscordBotCore.Online;
using DiscordBotCore.Others;
using Discord;
using DiscordBotCore.Networking;
using DiscordBotCore.PluginCore.Helpers.Execution.DbCommand;
using DiscordBotCore.PluginCore.Interfaces;
namespace MusicPlayer.Commands;
@@ -20,7 +19,7 @@ public class AddMelody: IDbCommand
public string Usage => "add_melody [title],[description?],[aliases],[byteSize]";
public bool RequireAdmin => false;
public async void ExecuteServer(DbCommandExecutingArguments args)
public async void ExecuteServer(IDbCommandExecutingArgument args)
{
var arguments = string.Join(" ", args.Arguments);
string[] split = arguments.Split(',');
@@ -68,19 +67,19 @@ public class AddMelody: IDbCommand
IProgress<float> downloadProgress = new Progress<float>();
var melodiesDirectory = Path.Combine(args.PluginBaseDirectory.FullName, "Music/Melodies");
var fileLocation = Path.Combine(melodiesDirectory, $"{title}.mp3");
Directory.CreateDirectory(melodiesDirectory);
FileDownloader downloader = new FileDownloader(file.Url, fileLocation);
await downloader.DownloadFile(downloadProgress.Report);
var location = Application.GetResourceFullPath($"Music/Melodies/{title}.mp3");
Directory.CreateDirectory(Application.GetResourceFullPath("Music/Melodies"));
await ServerCom.DownloadFileAsync(file.Url, location, downloadProgress);
Console.WriteLine($"Done. Saved at {location}");
args.Logger.Log($"Melody downloaded. Saved at {fileLocation}", this);
await msg.ModifyAsync(a => a.Content = "Done");
var info = MusicInfoExtensions.CreateMusicInfo(title, location, description ?? "Unknown", aliases.ToList(), bsize);
var info = MusicInfoExtensions.CreateMusicInfo(title, fileLocation, description ?? "Unknown", aliases.ToList(), bsize);
Variables._MusicDatabase?.Add(title, info);
Variables._MusicDatabase.Add(title, info);
var builder = new EmbedBuilder();
builder.Title = "A new music was successfully added !";

View File

@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\DiscordBotCore.PluginCore\DiscordBotCore.PluginCore.csproj" />
<ProjectReference Include="..\..\DiscordBotCore\DiscordBotCore.csproj" />
</ItemGroup>

View File

@@ -25,11 +25,8 @@ public class Play: IDbSlashCommand
}
};
public async void ExecuteServer(SocketSlashCommand context)
{
if(Variables._MusicDatabase is null)
{
await context.RespondAsync("Music Database is not loaded !");

View File

@@ -4,8 +4,8 @@ namespace MusicPlayer;
public class Variables
{
public static MusicDatabase? _MusicDatabase;
public static MusicPlayer? _MusicPlayer;
public static Dictionary<string, MusicInfo> _MusicDatabase;
public static MusicPlayer _MusicPlayer;
public static IAudioClient? audioClient;
public static IAudioClient audioClient;
}