From 8b7e8a0f32fe4a77f7bee81fcdad76f331a5cd31 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Thu, 17 Jul 2025 14:22:01 -0500 Subject: [PATCH] Add Discovery Commands and Dependencies --- .../Contracts/IAllowConfigurationAccessor.cs | 3 +- .../Contracts/IRoleConfigurationAccessor.cs | 17 ++++ .../AllowConfigurationAccessor.cs | 5 +- .../RoleConfigurationAccessor.cs | 79 +++++++++++++++++ .../Application/DiscoverySlashCommands.cs | 84 +++++++++++++++++++ .../Services/MessageInteractionService.cs | 2 +- src/CB.Data/CbContext.cs | 1 + 7 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 src/CB.Accessors/Contracts/IRoleConfigurationAccessor.cs create mode 100644 src/CB.Accessors/Implementations/RoleConfigurationAccessor.cs create mode 100644 src/CB.Bot/Commands/Application/DiscoverySlashCommands.cs diff --git a/src/CB.Accessors/Contracts/IAllowConfigurationAccessor.cs b/src/CB.Accessors/Contracts/IAllowConfigurationAccessor.cs index 52adc9a..bdae907 100644 --- a/src/CB.Accessors/Contracts/IAllowConfigurationAccessor.cs +++ b/src/CB.Accessors/Contracts/IAllowConfigurationAccessor.cs @@ -11,8 +11,7 @@ public interface IAllowConfigurationAccessor Task CreateAsync(AllowConfiguration entity); - Task UpdateAsync(string id, - AllowConfigurationDto entity); + Task UpdateAsync(AllowConfigurationDto entity); Task DeleteAsync(string id); } \ No newline at end of file diff --git a/src/CB.Accessors/Contracts/IRoleConfigurationAccessor.cs b/src/CB.Accessors/Contracts/IRoleConfigurationAccessor.cs new file mode 100644 index 0000000..aa66c7d --- /dev/null +++ b/src/CB.Accessors/Contracts/IRoleConfigurationAccessor.cs @@ -0,0 +1,17 @@ +using CB.Data.Entities; +using CB.Shared.Dtos; + +namespace CB.Accessors.Contracts; + +public interface IRoleConfigurationAccessor +{ + Task> GetAllAsync(); + + Task GetByIdAsync(string id); + + Task CreateAsync(RoleConfiguration entity); + + Task UpdateAsync(RoleConfigurationDto entity); + + Task DeleteAsync(string id); +} \ No newline at end of file diff --git a/src/CB.Accessors/Implementations/AllowConfigurationAccessor.cs b/src/CB.Accessors/Implementations/AllowConfigurationAccessor.cs index a3aa71b..044c338 100644 --- a/src/CB.Accessors/Implementations/AllowConfigurationAccessor.cs +++ b/src/CB.Accessors/Implementations/AllowConfigurationAccessor.cs @@ -34,12 +34,11 @@ await context return mapper.Map(entity); } - public async Task UpdateAsync(string id, - AllowConfigurationDto updated) + public async Task UpdateAsync(AllowConfigurationDto updated) { var allowConfiguration = await context .AllowConfigurations - .FindAsync(id) + .FirstOrDefaultAsync( x => x.GuildId == updated.GuildId) .ConfigureAwait(false); if (allowConfiguration == null) diff --git a/src/CB.Accessors/Implementations/RoleConfigurationAccessor.cs b/src/CB.Accessors/Implementations/RoleConfigurationAccessor.cs new file mode 100644 index 0000000..081f21d --- /dev/null +++ b/src/CB.Accessors/Implementations/RoleConfigurationAccessor.cs @@ -0,0 +1,79 @@ +using AutoMapper; +using AutoMapper.QueryableExtensions; +using CB.Accessors.Contracts; +using CB.Data; +using CB.Data.Entities; +using CB.Shared.Dtos; +using Microsoft.EntityFrameworkCore; + +namespace CB.Accessors.Implementations; + +public class RoleConfigurationAccessor(CbContext context, + IMapper mapper) + : IRoleConfigurationAccessor +{ + public Task> GetAllAsync() => context + .RoleConfigurations + .AsNoTracking() + .ProjectTo(mapper.ConfigurationProvider) + .ToListAsync(); + + public Task GetByIdAsync(string id) => context.RoleConfigurations + .AsNoTracking() + .Where(g => g.GuildId == id) + .ProjectTo(mapper.ConfigurationProvider) + .FirstOrDefaultAsync(); + + public async Task CreateAsync(RoleConfiguration entity) + { + context.RoleConfigurations.Add(entity); + await context + .SaveChangesAsync() + .ConfigureAwait(false); + + return mapper.Map(entity); + } + + public async Task UpdateAsync(RoleConfigurationDto updated) + { + var roleConfiguration = await context + .RoleConfigurations + .FirstOrDefaultAsync(x => x.GuildId == updated.GuildId) + .ConfigureAwait(false); + + if (roleConfiguration == null) + { + return null; + } + + roleConfiguration.DiscoveryRoleId = updated.DiscoveryRoleId; + roleConfiguration.JoinRoleId = updated.JoinRoleId; + roleConfiguration.LiveDiscoveryRoleId = updated.LiveDiscoveryRoleId; + + await context + .SaveChangesAsync() + .ConfigureAwait(false); + + return mapper.Map(roleConfiguration); + } + + public async Task DeleteAsync(string id) + { + var roleConfiguration = await context + .RoleConfigurations + .FindAsync(id) + .ConfigureAwait(false); + + if (roleConfiguration == null) + { + return false; + } + + context.RoleConfigurations.Remove(roleConfiguration); + await context + .SaveChangesAsync() + .ConfigureAwait(false); + + return true; + } +} \ No newline at end of file diff --git a/src/CB.Bot/Commands/Application/DiscoverySlashCommands.cs b/src/CB.Bot/Commands/Application/DiscoverySlashCommands.cs new file mode 100644 index 0000000..b496f5d --- /dev/null +++ b/src/CB.Bot/Commands/Application/DiscoverySlashCommands.cs @@ -0,0 +1,84 @@ +using CB.Accessors.Contracts; +using Discord; +using Discord.Interactions; + +// ReSharper disable UnusedMember.Local +// ReSharper disable UnusedMember.Global + +namespace CB.Bot.Commands.Application; + +[Group("discovery", "Discovery commands")] +public class DiscoverySlashCommands(IGuildAccessor guildAccessor, + IRoleConfigurationAccessor roleConfigurationAccessor, + IAllowConfigurationAccessor allowConfigurationAccessor) : BaseSlashCommands +{ +[SlashCommand( + "enable", + "Configure server 'Discovery' setting", + false, + RunMode.Async)] + private async Task DiscoveryEnableConfigurationAsync(IRole role = null) + { + await DoStuff(true, role); + } + + [SlashCommand( + "disable", + "Configure server 'Discovery' setting", + false, + RunMode.Async)] + private async Task DiscoveryDisableConfigurationAsync() + { + await DoStuff(false); + } + + private async Task DoStuff(bool isEnabled, + IRole role = null) + { + await SocketInteraction.DeferAsync(true); + + if (!await IsUserAdmin()) + { + return; + } + + var guildChannel = (IGuildChannel)SocketInteraction.Channel; + var guild = await guildAccessor.GetByIdAsync(guildChannel.Guild.Id.ToString()); + if (guild == null) + { + await SocketInteraction.FollowupAsync( + "Sorry, unable to configure Discovery for this guild. Contact support.", ephemeral: true); + return; + } + + string message; + if (!isEnabled) + { + guild.RoleConfiguration.DiscoveryRoleId = null; + guild.AllowConfiguration.AllowLiveDiscovery = false; + message = "Discovery has been set to `Disabled`."; + } + else + { + if (role != null) + { + guild.RoleConfiguration.DiscoveryRoleId = role.Id.ToString(); + guild.AllowConfiguration.AllowLiveDiscovery = true; + + message = $"Discovery has been set to role {role.Name}."; + } + else + { + guild.RoleConfiguration.DiscoveryRoleId = null; + guild.AllowConfiguration.AllowLiveDiscovery = true; + + message = "Discovery has been set to .. literally everyone!"; + } + } + + await roleConfigurationAccessor.UpdateAsync(guild.RoleConfiguration); + await allowConfigurationAccessor.UpdateAsync(guild.AllowConfiguration); + + await SocketInteraction.FollowupAsync(message, ephemeral: true); + } +} \ No newline at end of file diff --git a/src/CB.Bot/Services/MessageInteractionService.cs b/src/CB.Bot/Services/MessageInteractionService.cs index 1d65f4c..a4a8131 100644 --- a/src/CB.Bot/Services/MessageInteractionService.cs +++ b/src/CB.Bot/Services/MessageInteractionService.cs @@ -183,7 +183,7 @@ private async Task ProcessAllowDropdownAsync(ISocketMessageChannel channel, } } - await _allowConfigurationAccessor.UpdateAsync(guild.Id, guild.AllowConfiguration); + await _allowConfigurationAccessor.UpdateAsync(guild.AllowConfiguration); await _guildConfigurationAccessor.UpdateAsync(guild.Id, guild.GuildConfiguration); await message.FollowupAsync(response.ToString(), ephemeral: true); diff --git a/src/CB.Data/CbContext.cs b/src/CB.Data/CbContext.cs index 6add769..d22f457 100644 --- a/src/CB.Data/CbContext.cs +++ b/src/CB.Data/CbContext.cs @@ -16,6 +16,7 @@ public class CbContext(DbContextOptions options) : DbContext(options) public DbSet Guilds => Set(); public DbSet GuildConfigurations => Set(); public DbSet LiveEmbeds => Set(); + public DbSet RoleConfigurations => Set(); public DbSet Users => Set(); public DbSet VodEmbeds => Set();