Compare commits
3 commits
v2
...
v2DiscordB
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c44e66f0d4 | ||
![]() |
b503e6edeb | ||
![]() |
d140e50cbc |
10 changed files with 441 additions and 0 deletions
|
@ -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,46 @@ 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 PostChannelId { get; set; }
|
||||
|
||||
[JsonProperty("ThreadChannels")]
|
||||
[Description("This is reqired for Logging Don't Touch")]
|
||||
public List<long>? PostIds { 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/";
|
||||
}
|
||||
}
|
||||
}
|
26
Moonlight/App/Services/Discord/Bot/BaseModule.cs
Normal file
26
Moonlight/App/Services/Discord/Bot/BaseModule.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
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;
|
||||
if(command.User.IsBot) return;
|
||||
var dsc = Scope.ServiceProvider.GetRequiredService<DiscordBotService>();
|
||||
|
||||
//Global Commands
|
||||
switch (command.CommandName)
|
||||
{
|
||||
case "help": await dsc.HelpCommand.Handler(command); 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 "setup": await dsc.SetupCommand.Handler(command); break;
|
||||
}
|
||||
}
|
||||
|
||||
public override Task Init()
|
||||
{ throw new NotImplementedException(); }
|
||||
}
|
21
Moonlight/App/Services/Discord/Bot/Commands/HelpCommand.cs
Normal file
21
Moonlight/App/Services/Discord/Bot/Commands/HelpCommand.cs
Normal file
|
@ -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<EmbedBuilderModule>();
|
||||
}
|
||||
|
||||
public override Task Init()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
92
Moonlight/App/Services/Discord/Bot/Commands/SetupCommand.cs
Normal file
92
Moonlight/App/Services/Discord/Bot/Commands/SetupCommand.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using Discord;
|
||||
using Discord.Commands;
|
||||
using Discord.WebSocket;
|
||||
|
||||
namespace Moonlight.App.Services.Discord.Bot.Commands;
|
||||
|
||||
public class SetupCommand : BaseModule
|
||||
{
|
||||
public SetupCommand(DiscordSocketClient client, ConfigService configService, IServiceScope scope) : base(client, configService, scope)
|
||||
{ }
|
||||
|
||||
public async Task Handler(SocketSlashCommand command)
|
||||
{
|
||||
var dsc = Scope.ServiceProvider.GetRequiredService<DiscordBotService>();
|
||||
var guild = Client.GetGuild((ulong)DiscordConfig.GuildId);
|
||||
var user = guild.GetUser(command.User.Id);
|
||||
|
||||
// Permission check
|
||||
if (user is { GuildPermissions.Administrator: false } || guild.CurrentUser.GuildPermissions is { ManageChannels: false })
|
||||
{
|
||||
await command.RespondAsync(ephemeral: true, embed: dsc.EmbedBuilderModule.ErrorEmbed("Insufficient permissions", "You must have Administrator \n The Bot must have `Channel Edit` Permissions", command.User).Build());
|
||||
return;
|
||||
}
|
||||
|
||||
// Already setup
|
||||
if (guild.GetChannel((ulong)DiscordConfig.PostChannelId) != null)
|
||||
{
|
||||
await command.RespondAsync(ephemeral: true, embed: dsc.EmbedBuilderModule.ErrorEmbed("Already setup", "Setup canceled!", command.User).Build());
|
||||
return;
|
||||
}
|
||||
|
||||
// Automatic setup
|
||||
if (command.Data.Options.FirstOrDefault() != null && (bool)command.Data.Options.FirstOrDefault())
|
||||
{
|
||||
await command.RespondAsync(ephemeral: true, embed: dsc.EmbedBuilderModule.WarningEmbed("Automatic Setup", "This is the fast setup.\n Please wait...", command.User).Build());
|
||||
|
||||
var postChannel = await guild.CreateForumChannelAsync("NotifyChannel", x =>
|
||||
{
|
||||
x.DefaultLayout = ForumLayout.List;
|
||||
x.Flags = ChannelFlags.Pinned;
|
||||
x.PermissionOverwrites = new List<Overwrite>
|
||||
{
|
||||
new(guild.EveryoneRole.Id, PermissionTarget.Role, new OverwritePermissions(viewChannel: PermValue.Deny)),
|
||||
new(guild.CurrentUser.Id, PermissionTarget.User, new OverwritePermissions(viewChannel: PermValue.Allow))
|
||||
};
|
||||
});
|
||||
|
||||
var posts = new List<long>();
|
||||
|
||||
foreach (DiscordLogging x in Enum.GetValues(typeof(DiscordLogging)))
|
||||
{
|
||||
var post = await postChannel.CreatePostAsync( $"{x}", ThreadArchiveDuration.OneWeek,
|
||||
embed: dsc.EmbedBuilderModule.InfoEmbed($"{x}", "# Don't Remove the Channel \n Logging for ...", Client.CurrentUser).Build());
|
||||
|
||||
posts.Add((long)post.Id);
|
||||
}
|
||||
|
||||
//Save to Config
|
||||
ConfigService.Get().Discord.Bot.PostChannelId = (long)postChannel.Id;
|
||||
ConfigService.Get().Discord.Bot.PostIds = posts;
|
||||
ConfigService.Save();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await command.RespondAsync(ephemeral: true, embed: dsc.EmbedBuilderModule.SuccessEmbed("Manuel Setup", "This is the custom setup.", command.User).Build());
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override async Task Init()
|
||||
{
|
||||
var command = new SlashCommandBuilder
|
||||
{
|
||||
Name = "setup",
|
||||
Description = "Setup the bot and Channels",
|
||||
DefaultMemberPermissions = GuildPermission.ViewAuditLog,
|
||||
Options = new List<SlashCommandOptionBuilder>(new List<SlashCommandOptionBuilder>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Name = "fast",
|
||||
Description = "Fast Setup channel will be automatically created",
|
||||
Type = ApplicationCommandOptionType.Boolean,
|
||||
IsRequired = false
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
await Client.GetGuild(Convert.ToUInt64(DiscordConfig.GuildId)).CreateApplicationCommandAsync(command.Build());
|
||||
}
|
||||
}
|
131
Moonlight/App/Services/Discord/Bot/DiscordBotService.cs
Normal file
131
Moonlight/App/Services/Discord/Bot/DiscordBotService.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
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 EmbedBuilderModule EmbedBuilderModule { get; private set; }
|
||||
public CommandControllerModule CommandControllerModule { get; private set; }
|
||||
public HelpCommand HelpCommand { get; private set; }
|
||||
public SetupCommand SetupCommand { get; private set; }
|
||||
|
||||
|
||||
public DiscordBotService(IServiceScopeFactory serviceScopeFactory, ConfigService configService)
|
||||
{
|
||||
ServiceScopeFactory = serviceScopeFactory;
|
||||
ConfigService = configService;
|
||||
Client = new DiscordSocketClient( new DiscordSocketConfig
|
||||
{
|
||||
GatewayIntents = GatewayIntents.Guilds
|
||||
});
|
||||
|
||||
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);
|
||||
SetupCommand = new SetupCommand(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}");
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
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 Init()
|
||||
{
|
||||
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}");
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch.Stop();
|
||||
Logger.Debug($"Registered all commands. Done in {stopwatch.ElapsedMilliseconds}ms");
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
|
@ -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<List<FieldsModule>>? fields = null)
|
||||
{
|
||||
return StandardEmbed(title, message, Color.Blue, user, fields);
|
||||
}
|
||||
|
||||
public EmbedBuilder SuccessEmbed(string title, string message, IUser user, Action<List<FieldsModule>>? fields = null)
|
||||
{
|
||||
return StandardEmbed(title, message, Color.Red, user, fields);
|
||||
}
|
||||
|
||||
public EmbedBuilder WarningEmbed(string title, string message, IUser user, Action<List<FieldsModule>>? fields = null)
|
||||
{
|
||||
return StandardEmbed(title, message, Color.Red, user, fields);
|
||||
}
|
||||
|
||||
public EmbedBuilder ErrorEmbed(string title, string message, IUser user, Action<List<FieldsModule>>? fields = null)
|
||||
{
|
||||
return StandardEmbed(title, message, Color.Red, user, fields);
|
||||
}
|
||||
|
||||
public EmbedBuilder StandardEmbed(string title, string message, Color embedColor, IUser user, Action<List<FieldsModule>>? 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<FieldsModule>();
|
||||
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();
|
||||
}
|
||||
}
|
9
Moonlight/App/Services/Discord/DiscordLogging.cs
Normal file
9
Moonlight/App/Services/Discord/DiscordLogging.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Moonlight.App.Services.Discord;
|
||||
|
||||
public enum DiscordLogging
|
||||
{
|
||||
VeryImportant,
|
||||
System,
|
||||
Services,
|
||||
Ticket
|
||||
}
|
|
@ -18,12 +18,15 @@
|
|||
<Folder Include="App\Http\Middleware\" />
|
||||
<Folder Include="App\Http\Requests\" />
|
||||
<Folder Include="App\Http\Resources\" />
|
||||
<Folder Include="App\Services\Discord\WebHook\" />
|
||||
<Folder Include="storage\logs\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Ben.Demystifier" Version="0.4.1" />
|
||||
<PackageReference Include="BlazorTable" Version="1.17.0" />
|
||||
<PackageReference Include="Discord.Net" Version="3.12.0" />
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.12.0" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="8.0.746" />
|
||||
<PackageReference Include="JWT" Version="10.1.1" />
|
||||
<PackageReference Include="MailKit" Version="4.2.0" />
|
||||
|
|
|
@ -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<AutoMailSendService>();
|
|||
builder.Services.AddScoped<ServiceService>();
|
||||
builder.Services.AddSingleton<ServiceAdminService>();
|
||||
|
||||
// ThirdParty / Discord
|
||||
builder.Services.AddSingleton<DiscordBotService>();
|
||||
|
||||
// Services
|
||||
builder.Services.AddScoped<IdentityService>();
|
||||
builder.Services.AddSingleton(configService);
|
||||
|
@ -113,6 +118,7 @@ app.MapControllers();
|
|||
|
||||
// Auto start background services
|
||||
app.Services.GetRequiredService<AutoMailSendService>();
|
||||
app.Services.GetRequiredService<DiscordBotService>();
|
||||
|
||||
var serviceService = app.Services.GetRequiredService<ServiceAdminService>();
|
||||
await serviceService.RegisterAction(ServiceType.Server, new DummyActions());
|
||||
|
|
Loading…
Add table
Reference in a new issue