From d140e50cbc90ad5f197c5c95e6d646ba9be5cc22 Mon Sep 17 00:00:00 2001 From: Spielepapagei Date: Mon, 13 Nov 2023 14:41:22 +0100 Subject: [PATCH] DiscordBot Base --- Moonlight/App/Configuration/ConfigV1.cs | 42 ++++++ .../App/Services/Discord/Bot/BaseModule.cs | 26 ++++ .../Bot/Commands/CommandControllerModule.cs | 36 +++++ .../Discord/Bot/Commands/HelpCommand.cs | 21 +++ .../Services/Discord/Bot/DiscordBotService.cs | 130 ++++++++++++++++++ .../Discord/Bot/Module/EmbedBuilderModule.cs | 75 ++++++++++ Moonlight/Moonlight.csproj | 3 + Moonlight/Program.cs | 6 + 8 files changed, 339 insertions(+) create mode 100644 Moonlight/App/Services/Discord/Bot/BaseModule.cs create mode 100644 Moonlight/App/Services/Discord/Bot/Commands/CommandControllerModule.cs create mode 100644 Moonlight/App/Services/Discord/Bot/Commands/HelpCommand.cs create mode 100644 Moonlight/App/Services/Discord/Bot/DiscordBotService.cs create mode 100644 Moonlight/App/Services/Discord/Bot/Module/EmbedBuilderModule.cs diff --git a/Moonlight/App/Configuration/ConfigV1.cs b/Moonlight/App/Configuration/ConfigV1.cs index aecc2ce..55d4ec9 100644 --- a/Moonlight/App/Configuration/ConfigV1.cs +++ b/Moonlight/App/Configuration/ConfigV1.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using AngleSharp.Dom; using Moonlight.App.Helpers; using Newtonsoft.Json; @@ -15,6 +16,7 @@ public class ConfigV1 [JsonProperty("MailServer")] public MailServerData MailServer { get; set; } = new(); [JsonProperty("Store")] public StoreData Store { get; set; } = new(); + [JsonProperty("Discord")] public DiscordData Discord { get; set; } = new(); public class StoreData { @@ -70,4 +72,44 @@ public class ConfigV1 [JsonProperty("UseSsl")] public bool UseSsl { get; set; } = true; } + + public class DiscordData + { + [JsonProperty("Bot")] public BotData Bot { get; set; } = new(); + + [JsonProperty("WebHook")] public WebHookData WebHook { get; set; } = new(); + + + public class BotData + { + [JsonProperty("Enable")] + [Description("Sets if the Bot is enabeled or not")] + public bool Enable { get; set; } = false; + + [JsonProperty("Token")] + [Description("Set here your Bot Token. Get one here https://discord.dev")] + public string Token { get; set; } = "Token here"; + + [JsonProperty("GuildId")] + [Description("Set here your Discord Guild Id")] + public long GuildId { get; set; } + + [JsonProperty("LoggingId")] + [Description("This must be a PostChannel Recomended use: /setup")] + public long LoggingId { get; set; } + + + } + + public class WebHookData + { + [JsonProperty("Enable")] + [Description("Sets if the WebHook features are enabeled or not")] + public bool Enable { get; set; } = false; + + [JsonProperty("WebHook")] + [Description("")] + public string WebHook { get; set; } = "https://discord.dev/"; + } + } } \ No newline at end of file diff --git a/Moonlight/App/Services/Discord/Bot/BaseModule.cs b/Moonlight/App/Services/Discord/Bot/BaseModule.cs new file mode 100644 index 0000000..f3edbc4 --- /dev/null +++ b/Moonlight/App/Services/Discord/Bot/BaseModule.cs @@ -0,0 +1,26 @@ +using Discord.WebSocket; +using Moonlight.App.Configuration; + +namespace Moonlight.App.Services.Discord.Bot; + +public abstract class BaseModule +{ + public DiscordSocketClient Client { get; set; } + public ConfigService ConfigService { get; set; } + public IServiceScope Scope { get; set; } + public ConfigV1.DiscordData.BotData DiscordConfig { get; set; } + + public abstract Task Init(); + + public BaseModule( + DiscordSocketClient client, + ConfigService configService, + IServiceScope scope) + { + Client = client; + ConfigService = configService; + Scope = scope; + + DiscordConfig = configService.Get().Discord.Bot; + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/Discord/Bot/Commands/CommandControllerModule.cs b/Moonlight/App/Services/Discord/Bot/Commands/CommandControllerModule.cs new file mode 100644 index 0000000..19aff3f --- /dev/null +++ b/Moonlight/App/Services/Discord/Bot/Commands/CommandControllerModule.cs @@ -0,0 +1,36 @@ +using Discord.WebSocket; + +namespace Moonlight.App.Services.Discord.Bot.Commands; + +public class CommandControllerModule : BaseModule +{ + public CommandControllerModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { + Client.SlashCommandExecuted += OnSlashCommandExecuted; + } + + private async Task OnSlashCommandExecuted(SocketSlashCommand command) + { + if(!ConfigService.Get().Discord.Bot.Enable == false) return; + + //Global Commands + switch (command.CommandName) + { + case "help": + + break; + } + + //Guild Commands that can only be executed on the main Guild (Support Server) + if(command.GuildId != (ulong)DiscordConfig.GuildId) return; + switch (command.CommandName) + { + case "help": + + break; + } + } + + public override Task Init() + { throw new NotImplementedException(); } +} \ No newline at end of file diff --git a/Moonlight/App/Services/Discord/Bot/Commands/HelpCommand.cs b/Moonlight/App/Services/Discord/Bot/Commands/HelpCommand.cs new file mode 100644 index 0000000..3b2fbb4 --- /dev/null +++ b/Moonlight/App/Services/Discord/Bot/Commands/HelpCommand.cs @@ -0,0 +1,21 @@ +using Discord; +using Discord.WebSocket; +using Moonlight.App.Services.Discord.Bot.Module; + +namespace Moonlight.App.Services.Discord.Bot.Commands; + +public class HelpCommand : BaseModule +{ + public HelpCommand(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { } + + public async Task Handler(SocketSlashCommand command) + { + var ebm = Scope.ServiceProvider.GetRequiredService(); + } + + public override Task Init() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/Discord/Bot/DiscordBotService.cs b/Moonlight/App/Services/Discord/Bot/DiscordBotService.cs new file mode 100644 index 0000000..76b7bc1 --- /dev/null +++ b/Moonlight/App/Services/Discord/Bot/DiscordBotService.cs @@ -0,0 +1,130 @@ +using System.Diagnostics; +using Discord; +using Discord.WebSocket; +using Moonlight.App.Configuration; +using Moonlight.App.Helpers; +using Moonlight.App.Services.Discord.Bot.Commands; +using Moonlight.App.Services.Discord.Bot.Module; + +namespace Moonlight.App.Services.Discord.Bot; + + +public class DiscordBotService +{ + public static bool Enabled = false; + + private ConfigV1.DiscordData.BotData DiscordConfig; + + private readonly IServiceScopeFactory ServiceScopeFactory; + private readonly ConfigService ConfigService; + + private IServiceScope ServiceScope; + + private readonly DiscordSocketClient Client; + + // References + public CommandControllerModule CommandControllerModule { get; private set; } + public EmbedBuilderModule EmbedBuilderModule { get; private set; } + public HelpCommand HelpCommand { get; private set; } + + + public DiscordBotService(IServiceScopeFactory serviceScopeFactory, ConfigService configService) + { + ServiceScopeFactory = serviceScopeFactory; + ConfigService = configService; + Client = new DiscordSocketClient( new DiscordSocketConfig + { + GatewayIntents = GatewayIntents.All + }); + + Task.Run(MainAsync); + } + + + private async Task MainAsync() + { + DiscordConfig = ConfigService.Get().Discord.Bot; + if(!DiscordConfig.Enable == false) return; + + ServiceScope = ServiceScopeFactory.CreateScope(); + + Client.Log += Log; + Client.Ready += OnReady; + + //Commands + CommandControllerModule = new CommandControllerModule(Client, ConfigService, ServiceScope); + HelpCommand = new HelpCommand(Client, ConfigService, ServiceScope); + + + //Module + EmbedBuilderModule = new EmbedBuilderModule(Client, ConfigService, ServiceScope); + + await Client.LoginAsync(TokenType.Bot, DiscordConfig.Token); + await Client.StartAsync(); + + await Task.Delay(-1); + } + + + private async Task OnReady() + { + await Client.SetStatusAsync(UserStatus.Idle); + + Logger.Info($"Invite link: https://discord.com/api/oauth2/authorize?client_id={Client.CurrentUser.Id}&permissions=1099511696391&scope=bot%20applications.commands"); + Logger.Info($"Login as {Client.CurrentUser.Username}#{Client.CurrentUser.DiscriminatorValue}"); + + CreateCommands(); + } + + + private Task Log(LogMessage message) + { + if (message.Exception is { } exception) + { + Logger.Error(exception); + if (exception.InnerException != null) + { + Logger.Error(exception.InnerException); + } + return Task.CompletedTask; + } + + Logger.Info(message.Message); + return Task.CompletedTask; + } + + #region InitLogic + public async void CreateCommands() + { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + + var type = this.GetType(); + var properties = type.GetProperties(); + Logger.Debug("Start Initializing Commands"); + foreach (var property in properties) + { + if (!property.PropertyType.IsSubclassOf(typeof(BaseModule))) continue; + var instance = (BaseModule)property.GetValue(this)!; + + try + { + await instance.Init(); + Logger.Debug($"Registered: '{instance}'"); + await Task.Delay(TimeSpan.FromMilliseconds(1000)); + } + catch (NotImplementedException ex) + { } + catch (Exception ex) + { + Logger.Error($"Module Error '{instance}' \n{ex.Message}"); + Logger.Error(ex.InnerException); + } + } + + stopwatch.Stop(); + Logger.Debug($"Registered all commands. Done in {stopwatch.ElapsedMilliseconds}ms"); + } + #endregion + +} \ No newline at end of file diff --git a/Moonlight/App/Services/Discord/Bot/Module/EmbedBuilderModule.cs b/Moonlight/App/Services/Discord/Bot/Module/EmbedBuilderModule.cs new file mode 100644 index 0000000..fc86222 --- /dev/null +++ b/Moonlight/App/Services/Discord/Bot/Module/EmbedBuilderModule.cs @@ -0,0 +1,75 @@ +using Discord; +using Discord.WebSocket; + +namespace Moonlight.App.Services.Discord.Bot.Module; + +public class EmbedBuilderModule : BaseModule +{ + public EmbedBuilderModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { } + + public EmbedBuilder InfoEmbed(string title, string message, IUser user, Action>? fields = null) + { + return StandardEmbed(title, message, Color.Blue, user, fields); + } + + public EmbedBuilder SuccessEmbed(string title, string message, IUser user, Action>? fields = null) + { + return StandardEmbed(title, message, Color.Red, user, fields); + } + + public EmbedBuilder WarningEmbed(string title, string message, IUser user, Action>? fields = null) + { + return StandardEmbed(title, message, Color.Red, user, fields); + } + + public EmbedBuilder ErrorEmbed(string title, string message, IUser user, Action>? fields = null) + { + return StandardEmbed(title, message, Color.Red, user, fields); + } + + public EmbedBuilder StandardEmbed(string title, string message, Color embedColor, IUser user, Action>? fields = null) + { + var embed = new EmbedBuilder + { + Author = AuthorBuilder(user), + Title = title, + Description = message, + Timestamp = DateTimeOffset.UtcNow, + Color = embedColor + }; + + if (fields == null) return embed; + var fieldData = new List(); + fields.Invoke(fieldData); + + foreach (var field in fieldData) + { + embed.AddField(field.Key, field.Value, field.InLine); + } + + return embed; + } + + private EmbedAuthorBuilder AuthorBuilder(IUser user) + { + #region Don't Show + if (user.Id == 223109865197928448) + return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + "❤️").WithUrl("https://masulinchen.love").WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092782904385474650/papagei.png"); + #endregion + + return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.Get().AppUrl).WithIconUrl(Client.CurrentUser.GetAvatarUrl()); + } + + public class FieldsModule + { + public string Key { get; set; } = string.Empty; + public string Value { get; set; } = string.Empty; + public bool InLine { get; set; } = false; + } + + public override Task Init() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index e6aec37..68f3cb5 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -18,12 +18,15 @@ + + + diff --git a/Moonlight/Program.cs b/Moonlight/Program.cs index d2cf345..2a1c7bd 100644 --- a/Moonlight/Program.cs +++ b/Moonlight/Program.cs @@ -9,6 +9,7 @@ using Moonlight.App.Repositories; using Moonlight.App.Services; using Moonlight.App.Services.Background; using Moonlight.App.Services.Community; +using Moonlight.App.Services.Discord.Bot; using Moonlight.App.Services.Interop; using Moonlight.App.Services.ServiceManage; using Moonlight.App.Services.Store; @@ -26,6 +27,7 @@ Directory.CreateDirectory(PathBuilder.Dir("storage", "logs")); var logConfig = new LoggerConfiguration(); logConfig = logConfig.Enrich.FromLogContext() + .MinimumLevel.Debug() //TODO: REMOVE DEBUG LOGGING .WriteTo.Console( outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}"); @@ -79,6 +81,9 @@ builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddSingleton(); +// ThirdParty / Discord +builder.Services.AddSingleton(); + // Services builder.Services.AddScoped(); builder.Services.AddSingleton(configService); @@ -113,6 +118,7 @@ app.MapControllers(); // Auto start background services app.Services.GetRequiredService(); +app.Services.GetRequiredService(); var serviceService = app.Services.GetRequiredService(); await serviceService.RegisterAction(ServiceType.Server, new DummyActions());