From 2b6028d5313bb0c11bab4835127e9a06800079c5 Mon Sep 17 00:00:00 2001 From: Spielepapagei Date: Fri, 28 Apr 2023 21:25:47 +0200 Subject: [PATCH] Feature Complete Server Manager + Stuff + added PowerActions + added ServerLink + added EmbedBuilder + added useful common snippets + prepared Server Commands ~ Improved UI ~ fixed bugs on the fly ~ Cookies baked for the little... parrot --- .../Commands/ClearChannelCommand.cs | 86 ++++ .../DiscordBot/Commands/ServerListCommand.cs | 92 ++++ .../Services/DiscordBot/DiscordBotService.cs | 33 +- .../Modules/ActivityStatusModule.cs | 10 +- .../Modules/CommonComponentHandlerModule.cs | 29 ++ .../DiscordBot/Modules/EmbedBuilderModule.cs | 94 ++++ .../Modules/PermissionCheckModule.cs | 45 ++ ...andsModuels.cs => RemoveCommandsModule.cs} | 10 +- .../ServerListComponentHandlerModule.cs | 419 ++++++++++++++++++ Moonlight/Moonlight.csproj | 2 +- Moonlight/Properties/launchSettings.json | 4 +- .../Shared/Views/Admin/Sys/DiscordBot.razor | 1 + Moonlight/defaultstorage/configs/config.json | 5 +- 13 files changed, 813 insertions(+), 17 deletions(-) create mode 100644 Moonlight/App/Services/DiscordBot/Commands/ClearChannelCommand.cs create mode 100644 Moonlight/App/Services/DiscordBot/Commands/ServerListCommand.cs create mode 100644 Moonlight/App/Services/DiscordBot/Modules/CommonComponentHandlerModule.cs create mode 100644 Moonlight/App/Services/DiscordBot/Modules/EmbedBuilderModule.cs create mode 100644 Moonlight/App/Services/DiscordBot/Modules/PermissionCheckModule.cs rename Moonlight/App/Services/DiscordBot/Modules/{RemoveCommandsModuels.cs => RemoveCommandsModule.cs} (72%) create mode 100644 Moonlight/App/Services/DiscordBot/Modules/ServerListComponentHandlerModule.cs diff --git a/Moonlight/App/Services/DiscordBot/Commands/ClearChannelCommand.cs b/Moonlight/App/Services/DiscordBot/Commands/ClearChannelCommand.cs new file mode 100644 index 00000000..8660255b --- /dev/null +++ b/Moonlight/App/Services/DiscordBot/Commands/ClearChannelCommand.cs @@ -0,0 +1,86 @@ +using Discord; +using Discord.WebSocket; +using Logging.Net; + +namespace Moonlight.App.Services.DiscordBot.Commands; + +public class ClearChannelCommand : BaseModule +{ + public ClearChannelCommand(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { + Client.SlashCommandExecuted += ClearChannel; + Client.ButtonExecuted += ButtonHandler; + } + + private async Task ClearChannel(SocketSlashCommand command) + { + EmbedBuilder embed; + if(command.User.IsBot) return; + if(command.CommandName != "clear") return; + if(command.IsDMInteraction) + { + embed = new EmbedBuilder() + .WithAuthor($"Clear DM's > {command.User.Username}", Client.CurrentUser.GetAvatarUrl()) + .WithDescription($"Please press the button below. **This can't be undone!**") + .WithColor(Color.Purple); + + var button = new ComponentBuilder() + .WithButton("Clear", emote: new Emoji("🚮"), style: ButtonStyle.Danger, customId: $"clearDM"); + + await command.RespondAsync(embed: embed.Build(), components: button.Build()); + } + else + { + embed = new EmbedBuilder() + .WithAuthor($"Error > {command.User.Username}", Client.CurrentUser.GetAvatarUrl()) + .WithDescription($"Please use this Command here in your DM's") + .WithColor(Color.Red); + + await command.RespondAsync(embed: embed.Build(),ephemeral: true); + } + } + + private async Task ButtonHandler(SocketMessageComponent component) + { + if (component.Data.CustomId == "clearDM") + { + var button = new ComponentBuilder() + .WithButton("Clear", emote: new Emoji("🚮"), style: ButtonStyle.Danger, customId: $"clearDM", disabled: true); + + await component.RespondAsync("Please wait...", ephemeral: true); + + ulong userId = component.User.Id; + + int messagesDeleted = 0; + var dmChannel = await component.User.CreateDMChannelAsync(); + IEnumerable messages = await dmChannel.GetMessagesAsync().FlattenAsync(); + + foreach (var message in messages) + { + if (message.Author.IsBot) + { + await message.DeleteAsync(); + await Task.Delay(500); + messagesDeleted++; + } + } + + var embed = new EmbedBuilder() + .WithAuthor("Messages Delete!", Client.CurrentUser.GetAvatarUrl()) + .WithDescription($"I deleted {messagesDeleted} messages.") + .WithColor(Color.Green); + + IUserMessage response = await dmChannel.SendMessageAsync(embed: embed.Build()); + await Task.Delay(TimeSpan.FromSeconds(5)); + await response.DeleteAsync(); + } + } + public async override Task RegisterCommands() + { + var command = new SlashCommandBuilder() + .WithName("clear") + .WithDescription("Clear your DM Channel"); + + await Client.CreateGlobalApplicationCommandAsync(command.Build()); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/DiscordBot/Commands/ServerListCommand.cs b/Moonlight/App/Services/DiscordBot/Commands/ServerListCommand.cs new file mode 100644 index 00000000..68e7fada --- /dev/null +++ b/Moonlight/App/Services/DiscordBot/Commands/ServerListCommand.cs @@ -0,0 +1,92 @@ +using Discord; +using Discord.WebSocket; +using Logging.Net; +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Repositories; +using Moonlight.App.Repositories.Servers; + + +namespace Moonlight.App.Services.DiscordBot.Commands; + +public class ServerListCommand : BaseModule +{ + public ServerListCommand(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { + Client.SlashCommandExecuted += ServerMenuCommand; + } + private async Task ServerMenuCommand(SocketSlashCommand command) + { + EmbedBuilder embed; + ComponentBuilder components; + var dcs = Scope.ServiceProvider.GetRequiredService(); + + if (command.User.IsBot) return; + if (command.CommandName != "servers") return; + var usersRepo = Scope.ServiceProvider.GetRequiredService(); + var user = usersRepo.Get().FirstOrDefault(x => x.DiscordId == command.User.Id); + //var user = usersRepo.Get().FirstOrDefault(x => x.Id == 1); + + if (user == null) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, command.User); + components = new ComponentBuilder(); + components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue("AppUrl")); + + await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); + return; + } + var serversRepo = Scope.ServiceProvider.GetRequiredService(); + var servers = serversRepo.Get().Include(x => x.Owner).Where(x => x.Owner.Id == user.Id).ToList(); + + var selectOptions = new List(); + + foreach (var server in servers.Take(25)) + { + selectOptions.Add(new SelectMenuOptionBuilder() + .WithLabel($"{server.Id} - {server.Name}") + .WithEmote(Emote.Parse("<:server3:968614410228736070>")) + .WithValue(server.Id.ToString())); + } + + components = new ComponentBuilder(); + + components.WithSelectMenu( + "ServerSelectorList", + selectOptions, + "Select the server you want to edit."); + + components.WithButton("Panel", + emote: Emote.Parse(""), + style: ButtonStyle.Link, + url: $"{ConfigService.GetSection("Moonlight").GetValue("AppUrl")}"); + + if (servers.Count > 25) + { + components.WithButton("Previous-page", + emote: Emote.Parse("<:arrowLeft:1101182242908278817>"), + style: ButtonStyle.Secondary, + customId:"SmPreviousPage.0", + disabled: true); + + components.WithButton("Next-page", + emote: Emote.Parse("<:arrowRight:1101182245408096336>"), + style: ButtonStyle.Secondary, + customId:"SmNextPage.0", + disabled: false); + } + + embed = dcs.EmbedBuilderModule.StandardEmbed("Server Manager", Color.Blue, command.User); + + + await command.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); + } + + public override async Task RegisterCommands() + { + var command = new SlashCommandBuilder() + .WithName("servers") + .WithDescription("Creates a list from all your servers"); + + await Client.CreateGlobalApplicationCommandAsync(command.Build()); + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/DiscordBot/DiscordBotService.cs b/Moonlight/App/Services/DiscordBot/DiscordBotService.cs index dbb99687..5a64d70a 100644 --- a/Moonlight/App/Services/DiscordBot/DiscordBotService.cs +++ b/Moonlight/App/Services/DiscordBot/DiscordBotService.cs @@ -1,8 +1,8 @@ using System.Diagnostics; using Discord; +using Discord.Commands; using Discord.WebSocket; using Logging.Net; -using Moonlight.App.Repositories; using Moonlight.App.Services.DiscordBot.Commands; using Moonlight.App.Services.DiscordBot.Modules; @@ -20,7 +20,14 @@ public class DiscordBotService private readonly DiscordSocketClient Client; // References + public RemoveCommandsModule RemoveCommandsModule { get; private set; } + public PermissionCheckModule PermissionCheckModule { get; private set; } + public EmbedBuilderModule EmbedBuilderModule { get; private set; } + public ServerListCommand ServerListCommand { get; private set; } + public ServerListComponentHandlerModule ServerListComponentHandlerModule { get; private set; } public ActivityStatusModule ActivityStatusModule { get; private set; } + public CommonComponentHandlerModule CommonComponentHandlerModule { get; private set; } + public ClearChannelCommand ClearChannelCommand { get; private set; } public DiscordBotService( IServiceScopeFactory serviceScopeFactory, @@ -47,10 +54,17 @@ public DiscordBotService( Client.Log += Log; Client.Ready += OnReady; - //Commands - //Module + ServerListCommand = new(Client, ConfigService, ServiceScope); + ClearChannelCommand = new(Client, ConfigService, ServiceScope); + + //Commands + EmbedBuilderModule = new(Client, ConfigService, ServiceScope); + PermissionCheckModule = new(Client, ConfigService, ServiceScope); + RemoveCommandsModule = new(Client, ConfigService, ServiceScope); ActivityStatusModule = new(Client, ConfigService, ServiceScope); + ServerListComponentHandlerModule = new(Client, ConfigService, ServiceScope); + CommonComponentHandlerModule = new(Client, ConfigService, ServiceScope); await ActivityStatusModule.UpdateActivityStatusList(); @@ -63,6 +77,7 @@ public DiscordBotService( { //await Client.SetGameAsync(name: "https://endelon-hosting.de", type: ActivityType.Watching); await Client.SetStatusAsync(UserStatus.Idle); + //await Client.SetStatusAsync(UserStatus.Online); 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}"); @@ -72,7 +87,17 @@ public DiscordBotService( private Task Log(LogMessage message) { - Logger.Debug(message.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; } diff --git a/Moonlight/App/Services/DiscordBot/Modules/ActivityStatusModule.cs b/Moonlight/App/Services/DiscordBot/Modules/ActivityStatusModule.cs index 970fa474..175b72bf 100644 --- a/Moonlight/App/Services/DiscordBot/Modules/ActivityStatusModule.cs +++ b/Moonlight/App/Services/DiscordBot/Modules/ActivityStatusModule.cs @@ -29,10 +29,14 @@ public class ActivityStatusModule : BaseModule { while (await Timer.WaitForNextTickAsync()) { - Random rand = new Random(); - LoadingMessage random = LoadingMessages[rand.Next(LoadingMessages.Count)]; + String random = "https://endelon-hosting.de"; + if (LoadingMessages.Any()) + { + Random rand = new Random(); + random = LoadingMessages[rand.Next(LoadingMessages.Count)].Message; + } - await Client.SetGameAsync(random.Message, "https://www.endelon.team", ActivityType.Streaming); + await Client.SetGameAsync(random, "https://www.endelon.team", ActivityType.Streaming); } } diff --git a/Moonlight/App/Services/DiscordBot/Modules/CommonComponentHandlerModule.cs b/Moonlight/App/Services/DiscordBot/Modules/CommonComponentHandlerModule.cs new file mode 100644 index 00000000..eb4234f8 --- /dev/null +++ b/Moonlight/App/Services/DiscordBot/Modules/CommonComponentHandlerModule.cs @@ -0,0 +1,29 @@ +using Discord; +using Discord.WebSocket; + +namespace Moonlight.App.Services.DiscordBot.Modules; + +public class CommonComponentHandlerModule : BaseModule +{ + public CommonComponentHandlerModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { + Client.ButtonExecuted += CommonButtonHandler; + } + public override Task RegisterCommands() + { return Task.CompletedTask; } + + private async Task CommonButtonHandler(SocketMessageComponent component) + { + EmbedBuilder embed; + switch (component.Data.CustomId) + { + case "clear": + await component.Message.DeleteAsync(); + var dcs = Scope.ServiceProvider.GetRequiredService(); + embed = dcs.EmbedBuilderModule.StandardEmbed("Cleared!", Color.Green, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + + break; + } + } +} \ No newline at end of file diff --git a/Moonlight/App/Services/DiscordBot/Modules/EmbedBuilderModule.cs b/Moonlight/App/Services/DiscordBot/Modules/EmbedBuilderModule.cs new file mode 100644 index 00000000..74952b72 --- /dev/null +++ b/Moonlight/App/Services/DiscordBot/Modules/EmbedBuilderModule.cs @@ -0,0 +1,94 @@ +using Discord; +using Discord.WebSocket; +using Moonlight.App.Database.Entities; + +namespace Moonlight.App.Services.DiscordBot.Modules; + +public class EmbedBuilderModule : BaseModule +{ + + public EmbedBuilderModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { } + public override Task RegisterCommands() + { + return Task.CompletedTask; + } + + public EmbedBuilder StandardEmbed(string message, Color embedColor, IUser user, Dictionary? fields = null) + { + var embed = new EmbedBuilder + { + Author = AThing(user), + Description = message, + Timestamp = DateTimeOffset.UtcNow, + Color = embedColor + }; + + if (fields != null) + { + foreach (var field in fields) + { + embed.AddField(field.Key, field.Value); + } + } + + return embed; + } + + public EmbedBuilder ServerManagerEmbed(string message, Color embedColor, IUser user, Server server) + { + var embed = new EmbedBuilder + { + Author = AThing(user), + Description = message, + Timestamp = DateTimeOffset.UtcNow, + Color = embedColor + }; + + embed.AddField("Server Name", $"```{server.Id} - {server.Name}```", inline: true); + embed.AddField("Owner", $"```{server.Owner.FirstName} {server.Owner.LastName}```", inline: true); + embed.AddField("Node", $"```{server.Node.Name}```", inline: true); + embed.AddField("Cpu", $"```{server.Cpu.ToString()}```", inline: true); + embed.AddField("Ram", $"```{server.Memory.ToString()}```", inline: true); + embed.AddField("Disk", $"```{server.Cpu.ToString()}```", inline: true); + embed.AddField("\u200b", "\u200b"); + embed.AddField("Address", $"```{server.Node.Fqdn}:{server.MainAllocation.Port.ToString()}```", inline: false); + + + return embed; + } + + public EmbedBuilder ColorChangerServerManagerEmbed(Embed? oldEmbed, Color embedColor, IUser user) + { + var embed = new EmbedBuilder + { + Author = AThing(user), + Description = oldEmbed.Description, + Timestamp = DateTimeOffset.UtcNow, + Color = embedColor + }; + + foreach (var field in oldEmbed.Fields) + { + embed.AddField(field.Name, field.Value, field.Inline); + } + + return embed; + } + + private EmbedAuthorBuilder AThing(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 + + Random random = new Random(); + int[] randomNumbers = new int[] { 1, 3, 8, 11, 20 }; + + if (randomNumbers.Contains(random.Next(1, 24))) + return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username + " - The Rick version").WithUrl(ConfigService.GetSection("Moonlight").GetValue("AppUrl")).WithIconUrl("https://cdn.discordapp.com/attachments/750696464014901268/1092783310129860618/rick.gif"); + + return new EmbedAuthorBuilder().WithName(Client.CurrentUser.Username).WithUrl(ConfigService.GetSection("Moonlight").GetValue("AppUrl")).WithIconUrl(Client.CurrentUser.GetAvatarUrl()); + } +} diff --git a/Moonlight/App/Services/DiscordBot/Modules/PermissionCheckModule.cs b/Moonlight/App/Services/DiscordBot/Modules/PermissionCheckModule.cs new file mode 100644 index 00000000..ff7398dd --- /dev/null +++ b/Moonlight/App/Services/DiscordBot/Modules/PermissionCheckModule.cs @@ -0,0 +1,45 @@ +using Discord.WebSocket; +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Repositories; +using Moonlight.App.Repositories.Servers; + +namespace Moonlight.App.Services.DiscordBot.Modules; + +public class PermissionCheckModule : BaseModule +{ + + public PermissionCheckModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { } + public override Task RegisterCommands() + { return Task.CompletedTask; } + + public bool IsAdminByDiscordId(ulong discordId) + { + + var usersRepo = Scope.ServiceProvider.GetRequiredService(); + + var user = usersRepo.Get().FirstOrDefault(x => x.DiscordId == discordId); + + if (user != null) + { + return user.Admin; + } + + return false; + } + + public bool HasViewPermissionByDiscordId(ulong discordId, int serverId) + { + var serversRepo = Scope.ServiceProvider.GetRequiredService(); + + var server = serversRepo.Get().Include(x => x.Owner).FirstOrDefault(x => x.Id == serverId); + + if (server != null && server.Owner.DiscordId == discordId) + { + return true; + } + + return false; + } + +} \ No newline at end of file diff --git a/Moonlight/App/Services/DiscordBot/Modules/RemoveCommandsModuels.cs b/Moonlight/App/Services/DiscordBot/Modules/RemoveCommandsModule.cs similarity index 72% rename from Moonlight/App/Services/DiscordBot/Modules/RemoveCommandsModuels.cs rename to Moonlight/App/Services/DiscordBot/Modules/RemoveCommandsModule.cs index 90e05bab..f8ccfaf9 100644 --- a/Moonlight/App/Services/DiscordBot/Modules/RemoveCommandsModuels.cs +++ b/Moonlight/App/Services/DiscordBot/Modules/RemoveCommandsModule.cs @@ -1,17 +1,17 @@ using System.Diagnostics; -using Discord; using Discord.WebSocket; using Logging.Net; -namespace Moonlight.App.Services.DiscordBot.Commands; +namespace Moonlight.App.Services.DiscordBot.Modules; -public class RemoveCommandsModuels : BaseModule +public class RemoveCommandsModule : BaseModule { - public RemoveCommandsModuels(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) {} + public RemoveCommandsModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { } public override Task RegisterCommands() { return Task.CompletedTask; } - private async void VoidCommands() + public async void VoidCommands() { Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); diff --git a/Moonlight/App/Services/DiscordBot/Modules/ServerListComponentHandlerModule.cs b/Moonlight/App/Services/DiscordBot/Modules/ServerListComponentHandlerModule.cs new file mode 100644 index 00000000..1a529bc0 --- /dev/null +++ b/Moonlight/App/Services/DiscordBot/Modules/ServerListComponentHandlerModule.cs @@ -0,0 +1,419 @@ +using Discord; +using Discord.WebSocket; +using Logging.Net; +using Microsoft.EntityFrameworkCore; +using Moonlight.App.Models.Wings; +using Moonlight.App.Repositories; +using Moonlight.App.Repositories.Servers; + +namespace Moonlight.App.Services.DiscordBot.Modules; + +public class ServerListComponentHandlerModule : BaseModule +{ + public ServerListComponentHandlerModule(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope) + { + Client.SelectMenuExecuted += MenuHandler; + Client.ButtonExecuted += ManagerButtonHandler; + Client.ButtonExecuted += PagesButtonHandler; + Client.ModalSubmitted += ModalHandler; + } + + public override Task RegisterCommands() + { + return Task.CompletedTask; + } + + private async Task ManagerButtonHandler(SocketMessageComponent component) + { + var nodeService = Scope.ServiceProvider.GetRequiredService(); + var serverRepo = Scope.ServiceProvider.GetRequiredService(); + var dcs = Scope.ServiceProvider.GetRequiredService(); + var costomId = component.Data.CustomId.Split("."); + EmbedBuilder embed = dcs.EmbedBuilderModule.StandardEmbed("Something went terribly wrong! \n Mission failed please try again later.", Color.Red, component.User); + + if (costomId.Length < 3) return; + + if(costomId[0] is not "Sm") return; + + int id = int.Parse(costomId[2]); + var server = serverRepo.Get() + .Include(x => x.Owner) + .Include(x => x.Node) + .Include(x => x.MainAllocation) + .FirstOrDefault(x => x.Id == id); + + if (server == null) + { + await ErrorEmbedSnippet(component); + return; + } + + if (server.Owner.DiscordId != component.User.Id) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Is this your Server? I don't think so. \n Yes i did think of that.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + var data = await nodeService.GetStatus(server.Node); + + if (data == null) + { + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + var serverService = Scope.ServiceProvider.GetRequiredService(); + var serverDetails = await serverService.GetDetails(server); + + // serverDetails.State == "STATE" + // here a secret for masu :) look at the number on the left <<<--- + // starting + // running + // stopping + // offline + // installing + if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue("PowerActions") && costomId[1] is "Start" or "Restart" or "Stop" or "Kill" or "Update") + { + embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + await component.DeleteOriginalResponseAsync(); + return; + } + + if (!ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue("SendCommands") && costomId[1] is "SendCommand") + { + embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + await component.DeleteOriginalResponseAsync(); + return; + } + + if (serverDetails.State == "installing") + { + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), Color.Blue, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + + embed = dcs.EmbedBuilderModule.StandardEmbed("Server is in Installing \n please try again later.", Color.Blue, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + switch (costomId[1]) + { + case "Start": + if (serverDetails.State != "offline") + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Server is in a Invalid State \n please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + await serverService.SetPowerState(server, PowerSignal.Start); + + await component.DeferAsync(); + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), Color.Green, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + + break; + + + case "Restart": + if (serverDetails.State == "running") + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Server is in a Invalid State \n please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + await serverService.SetPowerState(server, PowerSignal.Restart); + + await component.DeferAsync(); + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), Color.Orange, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + + break; + + + case "Stop": + if (serverDetails.State is not ("starting" or "stopping")) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Server is in a Invalid State \n please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + await serverService.SetPowerState(server, PowerSignal.Stop); + + await component.DeferAsync(); + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), Color.Red, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + + break; + + + case "Kill": + if (serverDetails.State != "stopping") + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Server is in a Invalid State \n please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + await serverService.SetPowerState(server, PowerSignal.Kill); + + await component.DeferAsync(); + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), Color.DarkRed, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + + break; + + case "SendCommand": + if (serverDetails.State != "running") + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Server is not Online! \n The server must be Online", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + ModalBuilder modal = new ModalBuilder() + .WithTitle("Send Command To Server") + .WithCustomId($"Sm.SendCommand.{costomId[2]}") + .AddTextInput("Command", "Command", TextInputStyle.Short, "Type here Your Command", 1, 169, true); + + await component.RespondWithModalAsync(modal.Build()); + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), Color.Green, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + break; + + case "Update": + Color serverStateColor; + switch (serverDetails.State) + { + case "starting": + serverStateColor = Color.Orange; + break; + + case "running": + serverStateColor = Color.Green; + break; + + case "stopping": + serverStateColor = Color.Orange; + break; + + case "offline": + serverStateColor = Color.DarkerGrey; + break; + + default: + serverStateColor = Color.DarkPurple; + break; + } + await component.DeferAsync(); + embed = dcs.EmbedBuilderModule.ColorChangerServerManagerEmbed(component.Message.Embeds.FirstOrDefault(), serverStateColor, component.User); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + break; + } + } + + private async Task ModalHandler(SocketModal component) + { + var nodeService = Scope.ServiceProvider.GetRequiredService(); + var serverRepo = Scope.ServiceProvider.GetRequiredService(); + var dcs = Scope.ServiceProvider.GetRequiredService(); + var costomId = component.Data.CustomId.Split("."); + EmbedBuilder embed; + + if(costomId[0] != "Sm" && costomId[1] == "SendCommand") return; + + var details = component.Data.Components.FirstOrDefault(x => x.CustomId == "Command"); + + int id = int.Parse(costomId[2]); + var server = serverRepo.Get() + .Include(x => x.Owner) + .Include(x => x.Node) + .Include(x => x.MainAllocation) + .FirstOrDefault(x => x.Id == id); + + if (server == null || details == null) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry :( \n Something went wrong. \n Please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + if (server.Owner.DiscordId != component.User.Id) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Is this your Server? I don't think so. \n Yes i did think of that.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + var data = await nodeService.GetStatus(server.Node); + if (data == null) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("The node might be down. \n Please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + return; + } + + var serverService = Scope.ServiceProvider.GetRequiredService(); + Logger.Info(server.Id + " - " + server.Name); + Logger.Info(details.Value); + //Execute in console. + + embed = dcs.EmbedBuilderModule.StandardEmbed($"This feature is disabled for Security reasons! \n If you believe this is a error please contact the Administrators from this panel.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + await component.DeleteOriginalResponseAsync(); + return; + + + + + } + + private async Task PagesButtonHandler(SocketMessageComponent component) + { + ComponentBuilder components; + EmbedBuilder embed; + var dcs = Scope.ServiceProvider.GetRequiredService(); + var usersRepo = Scope.ServiceProvider.GetRequiredService(); + var serverRepo = Scope.ServiceProvider.GetRequiredService(); + var user = usersRepo.Get().FirstOrDefault(x => x.DiscordId == component.User.Id); + var costomId = component.Data.CustomId.Split("."); + + if (costomId.Length < 2) return; + int nextPage; + switch (costomId[0]) + { + case "SmPreviousPage": + nextPage = int.Parse(costomId[1]) -1; + break; + + case "SmNextPage": + nextPage = int.Parse(costomId[1]) +1; + break; + + default: + return; + } + + if (user == null) + { + embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry ;( \n Please first create and/or link a Account to Discord! \n Press the Button to register/log in.", Color.Red, component.User); + components = new ComponentBuilder(); + components.WithButton("Click Here", style: ButtonStyle.Link, url: ConfigService.GetSection("Moonlight").GetValue("AppUrl")); + + await component.RespondAsync(embed: embed.Build(), components: components.Build(), ephemeral: true); + return; + } + + var servers = serverRepo.Get().Include(x => x.Owner).Where(x => x.Owner.Id == user.Id).ToList(); + var selectOptions = new List(); + + foreach (var server in servers.Skip(nextPage * 25).Take(25).ToList()) + { + selectOptions.Add(new SelectMenuOptionBuilder() + .WithLabel($"{server.Id} - {server.Name}") + .WithEmote(Emote.Parse("<:server3:968614410228736070>")) + .WithValue(server.Id.ToString())); + } + + int totalPages = (int)Math.Ceiling((double)servers.Count / 25 -1); + bool lastPage = nextPage == totalPages; + bool firstPage = nextPage == 0; + + components = new ComponentBuilder(); + components.WithSelectMenu( + "ServerSelectorList", + selectOptions, + "Select the server you want to edit."); + + components.WithButton("Panel", + emote: Emote.Parse(""), + style: ButtonStyle.Link, + url: $"{ConfigService.GetSection("Moonlight").GetValue("AppUrl")}"); + + components.WithButton("Previous-page", + emote: Emote.Parse("<:ArrowLeft:1101547474180649030>"), + style: ButtonStyle.Secondary, + customId:$"SmPreviousPage.{nextPage}", + disabled: firstPage); + + components.WithButton("Next-page", + emote: Emote.Parse("<:ArrowRight:1101547475380228257>"), + style: ButtonStyle.Secondary, + customId:$"SmNextPage.{nextPage}", + disabled: lastPage); + + await component.DeferAsync(); + await component.ModifyOriginalResponseAsync(x => x.Components = components.Build()); + } + + private async Task MenuHandler(SocketMessageComponent component) + { + if (component.Data.CustomId != "ServerSelectorList") return; + + var dcs = Scope.ServiceProvider.GetRequiredService(); + if (!int.TryParse(component.Data.Values.FirstOrDefault(), out int serverId)) + { + await ErrorEmbedSnippet(component); + return; + } + + var serverRepo = Scope.ServiceProvider.GetRequiredService(); + var server = serverRepo.Get() + .Include(x => x.Owner) + .Include(x => x.Node) + .Include(x => x.MainAllocation) + .FirstOrDefault(x => x.Id == serverId); + + if (server == null || server.Owner.DiscordId != component.User.Id) + { + await ErrorEmbedSnippet(component); + return; + } + + var embed = dcs.EmbedBuilderModule.ServerManagerEmbed("You're server", Color.Blue, component.User, server); + + var components = new ComponentBuilder(); + + if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue("PowerActions")) + { + components.WithButton("Start", style: ButtonStyle.Success, customId: $"Sm.Start.{server.Id}", disabled: false); + components.WithButton("Restart", style: ButtonStyle.Primary, customId: $"Sm.Restart.{server.Id}", disabled: false); + components.WithButton("Stop", style: ButtonStyle.Danger, customId: $"Sm.Stop.{server.Id}", disabled: false); + components.WithButton("Kill", style: ButtonStyle.Danger, customId: $"Sm.Kill.{server.Id}", disabled: false); + } + + components.WithButton("Way2Server", + emote: Emote.Parse(""), + style: ButtonStyle.Link, + url: $"{ConfigService.GetSection("Moonlight").GetValue("AppUrl")}/server/{server.Uuid}"); + + components.WithButton("Update", + emote: Emote.Parse("<:refresh:1101547898803605605>"), + style: ButtonStyle.Secondary, + customId: $"Sm.Update.{server.Id}"); + + if (ConfigService.GetSection("Moonlight").GetSection("DiscordBot").GetValue("SendCommands")) + { + components.WithButton("SendCommand", + emote: Emote.Parse("<:Console:1101547358157819944>"), + style: ButtonStyle.Secondary, + customId: $"Sm.SendCommand.{server.Id}"); + } + + await component.DeferAsync(); + await component.ModifyOriginalResponseAsync(x => x.Embed = embed.Build()); + await component.ModifyOriginalResponseAsync(x => x.Components = components.Build()); + } + + private async Task ErrorEmbedSnippet(SocketMessageComponent component) + { + var dcs = Scope.ServiceProvider.GetRequiredService(); + + var embed = dcs.EmbedBuilderModule.StandardEmbed("Sorry :( \n Something went wrong. \n Please try again later.", Color.Red, component.User); + await component.RespondAsync(embed: embed.Build(), ephemeral: true); + } +} \ No newline at end of file diff --git a/Moonlight/Moonlight.csproj b/Moonlight/Moonlight.csproj index 1c684c61..22aba371 100644 --- a/Moonlight/Moonlight.csproj +++ b/Moonlight/Moonlight.csproj @@ -18,7 +18,7 @@ - + diff --git a/Moonlight/Properties/launchSettings.json b/Moonlight/Properties/launchSettings.json index acd90c6c..778ebef4 100644 --- a/Moonlight/Properties/launchSettings.json +++ b/Moonlight/Properties/launchSettings.json @@ -10,7 +10,7 @@ "profiles": { "Moonlight": { "commandName": "Project", - "launchBrowser": true, + "launchBrowser": false, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, @@ -26,7 +26,7 @@ }, "Docker": { "commandName": "Docker", - "launchBrowser": true, + "launchBrowser": false, "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}", "publishAllPorts": true, "useSSL": true diff --git a/Moonlight/Shared/Views/Admin/Sys/DiscordBot.razor b/Moonlight/Shared/Views/Admin/Sys/DiscordBot.razor index 282b78d2..fa8a0d20 100644 --- a/Moonlight/Shared/Views/Admin/Sys/DiscordBot.razor +++ b/Moonlight/Shared/Views/Admin/Sys/DiscordBot.razor @@ -34,6 +34,7 @@ private Task VoidCommands() { + DiscordBotService.RemoveCommandsModule.VoidCommands(); return Task.CompletedTask; } } \ No newline at end of file diff --git a/Moonlight/defaultstorage/configs/config.json b/Moonlight/defaultstorage/configs/config.json index 6e9ff6dd..79958b4b 100644 --- a/Moonlight/defaultstorage/configs/config.json +++ b/Moonlight/defaultstorage/configs/config.json @@ -9,8 +9,9 @@ "Username": "user_name" }, "DiscordBot": { - "Enable": "True", - "Token": "Discord.Token.Here" + "Enable": false, + "Token": "Discord.Token.Here", + "PowerActions": false }, "Domains": { "_comment": "Cloudflare Api Credentials",