Added API to DiscordBotCore
This commit is contained in:
@@ -80,7 +80,7 @@ public class Program
|
|||||||
private static async Task LoadComponents(string[] args)
|
private static async Task LoadComponents(string[] args)
|
||||||
{
|
{
|
||||||
await Application.CreateApplication();
|
await Application.CreateApplication();
|
||||||
|
|
||||||
AppUpdater updater = new AppUpdater();
|
AppUpdater updater = new AppUpdater();
|
||||||
Update? update = await updater.PrepareUpdate();
|
Update? update = await updater.PrepareUpdate();
|
||||||
if(update is not null)
|
if(update is not null)
|
||||||
@@ -120,7 +120,8 @@ public class Program
|
|||||||
!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("prefix"))
|
!Application.CurrentApplication.ApplicationEnvironmentVariables.ContainsKey("prefix"))
|
||||||
await Installer.GenerateStartupConfig();
|
await Installer.GenerateStartupConfig();
|
||||||
|
|
||||||
|
Application.InitializeThreadedApi();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
67
DiscordBotCore/API/ApiManager.cs
Normal file
67
DiscordBotCore/API/ApiManager.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.API.Endpoints;
|
||||||
|
using DiscordBotCore.API.Endpoints.PluginManagement;
|
||||||
|
using DiscordBotCore.Interfaces.API;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API;
|
||||||
|
|
||||||
|
public class ApiManager
|
||||||
|
{
|
||||||
|
private bool IsRunning { get; set; }
|
||||||
|
private List<IEndpoint> ApiEndpoints { get; }
|
||||||
|
|
||||||
|
public ApiManager()
|
||||||
|
{
|
||||||
|
ApiEndpoints = new List<IEndpoint>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result AddEndpoint(IEndpoint endpoint)
|
||||||
|
{
|
||||||
|
if (ApiEndpoints.Contains(endpoint) || ApiEndpoints.Exists(x => x.Path == endpoint.Path))
|
||||||
|
{
|
||||||
|
return Result.Failure("Endpoint already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiEndpoints.Add(endpoint);
|
||||||
|
return Result.Success();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveEndpoint(string endpointPath)
|
||||||
|
{
|
||||||
|
this.ApiEndpoints.RemoveAll(endpoint => endpoint.Path == endpointPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EndpointExists(string endpointPath)
|
||||||
|
{
|
||||||
|
return this.ApiEndpoints.Exists(endpoint => endpoint.Path == endpointPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AddBaseEndpoints()
|
||||||
|
{
|
||||||
|
AddEndpoint(new HomeEndpoint());
|
||||||
|
AddEndpoint(new PluginListEndpoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitializeApi()
|
||||||
|
{
|
||||||
|
if (IsRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
IsRunning = true;
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder();
|
||||||
|
var app = builder.Build();
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
EndpointManager manager = new EndpointManager(app);
|
||||||
|
foreach(IEndpoint endpoint in this.ApiEndpoints)
|
||||||
|
{
|
||||||
|
manager.MapEndpoint(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
await app.RunAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
34
DiscordBotCore/API/ApiResponse.cs
Normal file
34
DiscordBotCore/API/ApiResponse.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API;
|
||||||
|
|
||||||
|
public class ApiResponse
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
public bool Success { get; set; }
|
||||||
|
|
||||||
|
private ApiResponse(string message, bool success)
|
||||||
|
{
|
||||||
|
Message = message;
|
||||||
|
Success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApiResponse From(string message, bool success)
|
||||||
|
{
|
||||||
|
return new ApiResponse(message, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApiResponse Fail(string message)
|
||||||
|
{
|
||||||
|
return new ApiResponse(message, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ApiResponse Ok()
|
||||||
|
{
|
||||||
|
return new ApiResponse(string.Empty, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> ToJson() => await JsonManager.ConvertToJsonString(this);
|
||||||
|
|
||||||
|
}
|
||||||
80
DiscordBotCore/API/EndpointManager.cs
Normal file
80
DiscordBotCore/API/EndpointManager.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using DiscordBotCore.Interfaces.API;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API;
|
||||||
|
|
||||||
|
internal sealed class EndpointManager
|
||||||
|
{
|
||||||
|
private WebApplication _appBuilder;
|
||||||
|
internal EndpointManager(WebApplication appBuilder)
|
||||||
|
{
|
||||||
|
_appBuilder = appBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void MapEndpoint(IEndpoint endpoint)
|
||||||
|
{
|
||||||
|
switch (endpoint.HttpMethod)
|
||||||
|
{
|
||||||
|
case EndpointType.Get:
|
||||||
|
_appBuilder.MapGet(endpoint.Path, async context =>
|
||||||
|
{
|
||||||
|
//convert the context to a string
|
||||||
|
string jsonRequest = string.Empty;
|
||||||
|
if (context.Request.Body.CanRead)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
|
||||||
|
jsonRequest = await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await endpoint.HandleRequest(jsonRequest);
|
||||||
|
await context.Response.WriteAsync(await response.ToJson());
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case EndpointType.Put:
|
||||||
|
_appBuilder.MapPut(endpoint.Path, async context =>
|
||||||
|
{
|
||||||
|
string jsonRequest = string.Empty;
|
||||||
|
if (context.Request.Body.CanRead)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
|
||||||
|
jsonRequest = await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await endpoint.HandleRequest(jsonRequest);
|
||||||
|
await context.Response.WriteAsync(await response.ToJson());
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case EndpointType.Post:
|
||||||
|
_appBuilder.MapPost(endpoint.Path, async context =>
|
||||||
|
{
|
||||||
|
string jsonRequest = string.Empty;
|
||||||
|
if (context.Request.Body.CanRead)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
|
||||||
|
jsonRequest = await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await endpoint.HandleRequest(jsonRequest);
|
||||||
|
await context.Response.WriteAsync(await response.ToJson());
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case EndpointType.Delete:
|
||||||
|
_appBuilder.MapDelete(endpoint.Path, async context =>
|
||||||
|
{
|
||||||
|
string jsonRequest = string.Empty;
|
||||||
|
if (context.Request.Body.CanRead)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8);
|
||||||
|
jsonRequest = await reader.ReadToEndAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = await endpoint.HandleRequest(jsonRequest);
|
||||||
|
await context.Response.WriteAsync(await response.ToJson());
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
26
DiscordBotCore/API/Endpoints/HomeEndpoint.cs
Normal file
26
DiscordBotCore/API/Endpoints/HomeEndpoint.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Interfaces.API;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API.Endpoints;
|
||||||
|
|
||||||
|
internal class HomeEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
private static readonly string _HomeMessage = "Welcome to the DiscordBot API.";
|
||||||
|
public string Path => "/";
|
||||||
|
EndpointType IEndpoint.HttpMethod => EndpointType.Get;
|
||||||
|
public async Task<ApiResponse> HandleRequest(string? jsonText)
|
||||||
|
{
|
||||||
|
string response = _HomeMessage;
|
||||||
|
if (jsonText != string.Empty)
|
||||||
|
{
|
||||||
|
var json = await JsonManager.ConvertFromJson<Dictionary<string,string>>(jsonText!);
|
||||||
|
response += $"\n\nYou sent the following JSON:\n{string.Join("\n", json.Select(x => $"{x.Key}: {x.Value}"))}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResponse.From(response, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Interfaces.API;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
using DiscordBotCore.Plugin;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.HttpResults;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API.Endpoints.PluginManagement;
|
||||||
|
|
||||||
|
public class InstallPluginEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public string Path => "/api/plugin/install";
|
||||||
|
public EndpointType HttpMethod => EndpointType.Put;
|
||||||
|
public async Task<ApiResponse> HandleRequest(string? jsonRequest)
|
||||||
|
{
|
||||||
|
Dictionary<string, string> jsonDict = await JsonManager.ConvertFromJson<Dictionary<string, string>>(jsonRequest);
|
||||||
|
string pluginName = jsonDict["pluginName"];
|
||||||
|
|
||||||
|
PluginOnlineInfo? pluginInfo = await Application.CurrentApplication.PluginManager.GetPluginDataByName(pluginName);
|
||||||
|
|
||||||
|
if (pluginInfo == null)
|
||||||
|
{
|
||||||
|
return ApiResponse.Fail("Plugin not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await Application.CurrentApplication.PluginManager.InstallPlugin(pluginInfo, null);
|
||||||
|
return ApiResponse.Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Interfaces.API;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API.Endpoints.PluginManagement;
|
||||||
|
|
||||||
|
public class PluginListEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public string Path => "/api/plugin/list/online";
|
||||||
|
public EndpointType HttpMethod => EndpointType.Get;
|
||||||
|
public async Task<ApiResponse> HandleRequest(string? jsonRequest)
|
||||||
|
{
|
||||||
|
var onlineInfos = await Application.CurrentApplication.PluginManager.GetPluginsList();
|
||||||
|
var response = await JsonManager.ConvertToJsonString(onlineInfos);
|
||||||
|
return ApiResponse.From(response, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.Interfaces.API;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.API.Endpoints.PluginManagement;
|
||||||
|
|
||||||
|
public class PluginListInstalledEndpoint : IEndpoint
|
||||||
|
{
|
||||||
|
public string Path => "/api/plugin/list/local";
|
||||||
|
public EndpointType HttpMethod => EndpointType.Get;
|
||||||
|
public async Task<ApiResponse> HandleRequest(string? jsonRequest)
|
||||||
|
{
|
||||||
|
var listInstalled = await Application.CurrentApplication.PluginManager.GetInstalledPlugins();
|
||||||
|
var response = await JsonManager.ConvertToJsonString(listInstalled);
|
||||||
|
return ApiResponse.From(response, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.API;
|
||||||
using DiscordBotCore.Bot;
|
using DiscordBotCore.Bot;
|
||||||
using DiscordBotCore.Interfaces.Logger;
|
using DiscordBotCore.Interfaces.Logger;
|
||||||
using DiscordBotCore.Online;
|
using DiscordBotCore.Online;
|
||||||
@@ -45,6 +46,7 @@ namespace DiscordBotCore
|
|||||||
public InternalActionManager InternalActionManager { get; private set; } = null!;
|
public InternalActionManager InternalActionManager { get; private set; } = null!;
|
||||||
public PluginManager PluginManager { get; private set; } = null!;
|
public PluginManager PluginManager { get; private set; } = null!;
|
||||||
public ILogger Logger { get; private set; } = null!;
|
public ILogger Logger { get; private set; } = null!;
|
||||||
|
public ApiManager? ApiManager { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create the application. This method is used to initialize the application. Can not initialize multiple times.
|
/// Create the application. This method is used to initialize the application. Can not initialize multiple times.
|
||||||
@@ -90,9 +92,32 @@ namespace DiscordBotCore
|
|||||||
|
|
||||||
CurrentApplication.InternalActionManager = new InternalActionManager();
|
CurrentApplication.InternalActionManager = new InternalActionManager();
|
||||||
await CurrentApplication.InternalActionManager.Initialize();
|
await CurrentApplication.InternalActionManager.Initialize();
|
||||||
|
|
||||||
IsRunning = true;
|
IsRunning = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the API in a separate thread
|
||||||
|
/// </summary>
|
||||||
|
public static void InitializeThreadedApi()
|
||||||
|
{
|
||||||
|
if (CurrentApplication is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(CurrentApplication.ApiManager is not null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentApplication.ApiManager = new ApiManager();
|
||||||
|
CurrentApplication.ApiManager.AddBaseEndpoints();
|
||||||
|
|
||||||
|
Thread apiThread = new Thread(() => _ = CurrentApplication.ApiManager.InitializeApi());
|
||||||
|
apiThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
public static string GetResourceFullPath(string path)
|
public static string GetResourceFullPath(string path)
|
||||||
{
|
{
|
||||||
var result = Path.Combine(_ResourcesFolder, path);
|
var result = Path.Combine(_ResourcesFolder, path);
|
||||||
|
|||||||
@@ -1,19 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<DebugType>portable</DebugType>
|
|
||||||
<DebugSymbols>false</DebugSymbols>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Config.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="BlankWindow1.xaml" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Discord.Net" Version="3.15.3" />
|
<PackageReference Include="Discord.Net" Version="3.15.3" />
|
||||||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.118" />
|
||||||
@@ -21,4 +11,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<UpToDateCheckInput Remove="UI\Controls\MessageBox.axaml" />
|
<UpToDateCheckInput Remove="UI\Controls\MessageBox.axaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="API\Services\" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
20
DiscordBotCore/Interfaces/API/IEndpoint.cs
Normal file
20
DiscordBotCore/Interfaces/API/IEndpoint.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using DiscordBotCore.API;
|
||||||
|
using DiscordBotCore.Others;
|
||||||
|
|
||||||
|
namespace DiscordBotCore.Interfaces.API;
|
||||||
|
|
||||||
|
public enum EndpointType
|
||||||
|
{
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Delete
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IEndpoint
|
||||||
|
{
|
||||||
|
public string Path { get; }
|
||||||
|
public EndpointType HttpMethod { get; }
|
||||||
|
public Task<ApiResponse> HandleRequest(string? jsonRequest);
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ public sealed class Logger : ILogger
|
|||||||
public Logger(string logFolder, string logMessageFormat, Action<string, LogType>? outFunction = null)
|
public Logger(string logFolder, string logMessageFormat, Action<string, LogType>? outFunction = null)
|
||||||
{
|
{
|
||||||
this.LogMessageFormat = logMessageFormat;
|
this.LogMessageFormat = logMessageFormat;
|
||||||
var logFile = logFolder + DateTime.Now.ToString("yyyy-MM-dd") + ".log";
|
var logFile = Path.Combine(logFolder, $"{DateTime.Now:yyyy-MM-dd}.log");
|
||||||
_LogFileStream = File.Open(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
|
_LogFileStream = File.Open(logFile, FileMode.Append, FileAccess.Write, FileShare.Read);
|
||||||
this._OutFunction = outFunction ?? DefaultLogFunction;
|
this._OutFunction = outFunction ?? DefaultLogFunction;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,20 @@ namespace DiscordBotCore.Others;
|
|||||||
|
|
||||||
public static class JsonManager
|
public static class JsonManager
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public static async Task<string> ConvertToJsonString<T>(T Data)
|
||||||
|
{
|
||||||
|
var str = new MemoryStream();
|
||||||
|
await JsonSerializer.SerializeAsync(str, Data, typeof(T), new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
WriteIndented = false,
|
||||||
|
});
|
||||||
|
var result = Encoding.ASCII.GetString(str.ToArray());
|
||||||
|
await str.FlushAsync();
|
||||||
|
str.Close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save to JSON file
|
/// Save to JSON file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user