diff --git a/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs b/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs
index 909ac6cd4..0288f6ffd 100644
--- a/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs
+++ b/src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs
@@ -2,17 +2,14 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-
using Discord;
-
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
-
-using Modix.Bot.Extensions;
using Modix.Common.Extensions;
using Modix.Data.Models.Core;
using Modix.Data.Models.Moderation;
using Modix.Data.Repositories;
+using Modix.Services;
using Modix.Services.Core;
using Modix.Services.Moderation;
using Modix.Services.Utilities;
@@ -24,15 +21,19 @@ namespace Modix.Behaviors
///
public class ModerationLoggingBehavior : IModerationActionEventHandler
{
+ private readonly DiscordRelayService _discordRelayService;
+
///
/// Constructs a new object, with injected dependencies.
///
public ModerationLoggingBehavior(
IServiceProvider serviceProvider,
IDiscordClient discordClient,
- IDesignatedChannelService designatedChannelService,
+ DesignatedChannelService designatedChannelService,
+ DiscordRelayService discordRelayService,
IOptions config)
{
+ _discordRelayService = discordRelayService;
DiscordClient = discordClient;
DesignatedChannelService = designatedChannelService;
Config = config.Value;
@@ -43,7 +44,9 @@ public ModerationLoggingBehavior(
///
public async Task OnModerationActionCreatedAsync(long moderationActionId, ModerationActionCreationData data)
{
- if (!await DesignatedChannelService.AnyDesignatedChannelAsync(data.GuildId, DesignatedChannelType.ModerationLog))
+ var designatedChannels = await DesignatedChannelService.GetDesignatedChannelIds(data.GuildId, DesignatedChannelType.ModerationLog);
+
+ if (!designatedChannels.Any())
return;
var moderationAction = await ModerationService.GetModerationActionSummaryAsync(moderationActionId);
@@ -73,8 +76,10 @@ public async Task OnModerationActionCreatedAsync(long moderationActionId, Modera
moderationAction.OriginalInfractionReason,
string.IsNullOrEmpty(moderationAction.Infraction?.RescindReason) ? "" : $"for reason: ```\n{moderationAction.Infraction?.RescindReason}```");
- await DesignatedChannelService.SendToDesignatedChannelsAsync(
- await DiscordClient.GetGuildAsync(data.GuildId), DesignatedChannelType.ModerationLog, message);
+ foreach (var channel in designatedChannels)
+ {
+ await _discordRelayService.SendMessageToChannel(channel, message);
+ }
}
///
@@ -85,7 +90,7 @@ await DesignatedChannelService.SendToDesignatedChannelsAsync(
///
/// An for logging moderation actions.
///
- internal protected IDesignatedChannelService DesignatedChannelService { get; }
+ internal protected DesignatedChannelService DesignatedChannelService { get; }
///
/// An for performing moderation actions.
diff --git a/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs b/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs
index 4c2a6f307..205f47c31 100644
--- a/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs
+++ b/src/Modix.Bot/Behaviors/PromotionLoggingHandler.cs
@@ -12,6 +12,7 @@
using Modix.Common.Messaging;
using Modix.Data.Models.Core;
using Modix.Data.Models.Promotions;
+using Modix.Services;
using Modix.Services.Core;
using Modix.Services.Promotions;
using Modix.Services.Utilities;
@@ -24,17 +25,21 @@ namespace Modix.Behaviors
public class PromotionLoggingHandler :
INotificationHandler
{
+ private readonly DiscordRelayService _discordRelayService;
+
///
/// Constructs a new object, with injected dependencies.
///
public PromotionLoggingHandler(
IAuthorizationService authorizationService,
DiscordSocketClient discordSocketClient,
- IDesignatedChannelService designatedChannelService,
+ DesignatedChannelService designatedChannelService,
IUserService userService,
IPromotionsService promotionsService,
+ DiscordRelayService discordRelayService,
IOptions modixConfig)
{
+ _discordRelayService = discordRelayService;
AuthorizationService = authorizationService;
DiscordSocketClient = discordSocketClient;
DesignatedChannelService = designatedChannelService;
@@ -49,26 +54,36 @@ public async Task HandleNotificationAsync(PromotionActionCreatedNotification not
if (AuthorizationService.CurrentUserId is null)
await AuthorizationService.OnAuthenticatedAsync(DiscordSocketClient.CurrentUser);
- if (await DesignatedChannelService.AnyDesignatedChannelAsync(notification.Data.GuildId, DesignatedChannelType.PromotionLog))
+ if (await DesignatedChannelService.HasDesignatedChannelForType(notification.Data.GuildId, DesignatedChannelType.PromotionLog))
{
var message = await FormatPromotionLogEntryAsync(notification.Id);
if (message == null)
return;
- await DesignatedChannelService.SendToDesignatedChannelsAsync(
- await DiscordSocketClient.GetGuildAsync(notification.Data.GuildId), DesignatedChannelType.PromotionLog, message);
+ var designatedChannels = await DesignatedChannelService.GetDesignatedChannelIds(notification.Data.GuildId,
+ DesignatedChannelType.PromotionLog);
+
+ foreach (var channel in designatedChannels)
+ {
+ await _discordRelayService.SendMessageToChannel(channel, message);
+ }
}
- if (await DesignatedChannelService.AnyDesignatedChannelAsync(notification.Data.GuildId, DesignatedChannelType.PromotionNotifications))
+ if (await DesignatedChannelService.HasDesignatedChannelForType(notification.Data.GuildId, DesignatedChannelType.PromotionNotifications))
{
var embed = await FormatPromotionNotificationAsync(notification.Id, notification.Data);
if (embed == null)
return;
- await DesignatedChannelService.SendToDesignatedChannelsAsync(
- await DiscordSocketClient.GetGuildAsync(notification.Data.GuildId), DesignatedChannelType.PromotionNotifications, "", embed);
+ var designatedChannels = await DesignatedChannelService.GetDesignatedChannelIds(notification.Data.GuildId,
+ DesignatedChannelType.PromotionNotifications);
+
+ foreach (var channel in designatedChannels)
+ {
+ await _discordRelayService.SendMessageToChannel(channel, string.Empty, embed);
+ }
}
}
@@ -144,7 +159,7 @@ private async Task FormatPromotionLogEntryAsync(long promotionActionId)
///
/// An for logging moderation actions.
///
- internal protected IDesignatedChannelService DesignatedChannelService { get; }
+ internal protected DesignatedChannelService DesignatedChannelService { get; }
///
/// An for retrieving user info
diff --git a/src/Modix.Bot/Handlers/SendMessageHandler.cs b/src/Modix.Bot/Handlers/SendMessageHandler.cs
new file mode 100644
index 000000000..1daade385
--- /dev/null
+++ b/src/Modix.Bot/Handlers/SendMessageHandler.cs
@@ -0,0 +1,25 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Discord;
+using Discord.WebSocket;
+using MediatR;
+using Modix.Services;
+
+namespace Modix.Bot.Handlers;
+
+public class SendMessageHandler(DiscordSocketClient discordSocketClient)
+ : IRequestHandler
+{
+ public async Task Handle(SendMessageInDiscordRequest request, CancellationToken cancellationToken)
+ {
+ var channel = await discordSocketClient.GetChannelAsync(request.ChannelId);
+
+ if (channel is ITextChannel textChannel)
+ {
+ await textChannel.SendMessageAsync(request.Message, false, request.Embed, allowedMentions: AllowedMentions.None);
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Modix.Bot/Modules/DesignatedChannelModule.cs b/src/Modix.Bot/Modules/DesignatedChannelsModule.cs
similarity index 80%
rename from src/Modix.Bot/Modules/DesignatedChannelModule.cs
rename to src/Modix.Bot/Modules/DesignatedChannelsModule.cs
index e1539ffb3..2b49c92b2 100644
--- a/src/Modix.Bot/Modules/DesignatedChannelModule.cs
+++ b/src/Modix.Bot/Modules/DesignatedChannelsModule.cs
@@ -9,20 +9,20 @@
using Modix.Bot.Preconditions;
using Modix.Common.Extensions;
using Modix.Data.Models.Core;
+using Modix.Services;
using Modix.Services.CommandHelp;
-using Modix.Services.Core;
-namespace Modix.Modules
+namespace Modix.Bot.Modules
{
[ModuleHelp("Channel Designations", "Configures channel designation for various bot services.")]
[Group("channel-designations", "Configures channel designation for various bot services.")]
[DefaultMemberPermissions(GuildPermission.BanMembers)]
- public class DesignatedChannelModule : InteractionModuleBase
+ public class DesignatedChannelsModule : InteractionModuleBase
{
- private readonly IDesignatedChannelService _designatedChannelService;
+ private readonly DesignatedChannelService _designatedChannelService;
private readonly ModixConfig _config;
- public DesignatedChannelModule(IDesignatedChannelService designatedChannelService, IOptions config)
+ public DesignatedChannelsModule(DesignatedChannelService designatedChannelService, IOptions config)
{
_designatedChannelService = designatedChannelService;
_config = config.Value;
@@ -30,24 +30,27 @@ public DesignatedChannelModule(IDesignatedChannelService designatedChannelServic
[SlashCommand("list", "Lists all of the channels designated for use by the bot.")]
[RequireClaims(AuthorizationClaim.DesignatedChannelMappingRead)]
- public async Task ListAsync()
+ public async Task List()
{
- var channels = await _designatedChannelService.GetDesignatedChannelsAsync(Context.Guild.Id);
-
- // https://mod.gg/config/channels
- var url = new UriBuilder(_config.WebsiteBaseUrl)
- {
- Path = "/config/channels"
- }.RemoveDefaultPort().ToString();
+ var channels = await _designatedChannelService.GetDesignatedChannels(Context.Guild.Id);
var builder = new EmbedBuilder()
{
Title = "Assigned Channel Designations",
- Url = url,
Color = Color.Gold,
Timestamp = DateTimeOffset.UtcNow
};
+ if (!string.IsNullOrWhiteSpace(_config.WebsiteBaseUrl))
+ {
+ var url = new UriBuilder(_config.WebsiteBaseUrl)
+ {
+ Path = "/config/channels"
+ }.RemoveDefaultPort().ToString();
+
+ builder.Url = url;
+ }
+
foreach (var type in Enum.GetValues())
{
var designatedChannels = channels
@@ -69,25 +72,25 @@ public async Task ListAsync()
[SlashCommand("add", "Assigns a designation to the given channel.")]
[RequireClaims(AuthorizationClaim.DesignatedChannelMappingCreate)]
- public async Task AddAsync(
+ public async Task Add(
[Summary(description: "The channel to be assigned a designation.")]
IMessageChannel channel,
[Summary(description: "The designation to assign.")]
DesignatedChannelType designation)
{
- await _designatedChannelService.AddDesignatedChannelAsync(Context.Guild, channel, designation);
+ await _designatedChannelService.AddDesignatedChannel(Context.Guild, channel, designation);
await Context.AddConfirmationAsync();
}
[SlashCommand("remove", "Removes a designation from the given channel.")]
[RequireClaims(AuthorizationClaim.DesignatedChannelMappingDelete)]
- public async Task RemoveAsync(
+ public async Task Remove(
[Summary(description: "The channel whose designation is to be unassigned.")]
IMessageChannel channel,
[Summary(description: "The designation to be unassigned.")]
DesignatedChannelType designation)
{
- await _designatedChannelService.RemoveDesignatedChannelAsync(Context.Guild, channel, designation);
+ await _designatedChannelService.RemoveDesignatedChannel(Context.Guild, channel, designation);
await Context.AddConfirmationAsync();
}
}
diff --git a/src/Modix.Bot/Modules/InfractionModule.cs b/src/Modix.Bot/Modules/InfractionModule.cs
index b353107d2..bea873291 100644
--- a/src/Modix.Bot/Modules/InfractionModule.cs
+++ b/src/Modix.Bot/Modules/InfractionModule.cs
@@ -86,7 +86,6 @@ public async Task SearchAsync(
var counts = await _moderationService.GetInfractionCountsForUserAsync(user.Id);
- // https://modix.gg/infractions?subject=12345
var url = new UriBuilder(_config.WebsiteBaseUrl)
{
Path = "/infractions",
diff --git a/src/Modix.Bot/Responders/StarboardReactionResponder.cs b/src/Modix.Bot/Responders/StarboardReactionResponder.cs
index 51965b670..79501474c 100644
--- a/src/Modix.Bot/Responders/StarboardReactionResponder.cs
+++ b/src/Modix.Bot/Responders/StarboardReactionResponder.cs
@@ -5,13 +5,13 @@
using Modix.Bot.Notifications;
using Modix.Bot.Responders.MessageQuotes;
using Modix.Data.Models.Core;
-using Modix.Services.Core;
+using Modix.Services;
using Modix.Services.Starboard;
using Modix.Services.Utilities;
namespace Modix.Bot.Responders
{
- public class StarboardReactionResponder(IStarboardService starboardService, IDesignatedChannelService designatedChannelService)
+ public class StarboardReactionResponder(IStarboardService starboardService, DesignatedChannelService designatedChannelService)
: INotificationHandler, INotificationHandler
{
public Task Handle(ReactionAddedNotificationV3 notification, CancellationToken cancellationToken)
@@ -35,10 +35,10 @@ private async Task HandleReactionAsync(Cacheable cachedMess
}
var isIgnoredFromStarboard = await designatedChannelService
- .ChannelHasDesignationAsync(channel.Guild.Id, channel.Id, DesignatedChannelType.IgnoredFromStarboard, default);
+ .ChannelHasDesignation(channel.Guild.Id, channel.Id, DesignatedChannelType.IgnoredFromStarboard, default);
var starboardExists = await designatedChannelService
- .AnyDesignatedChannelAsync(channel.GuildId, DesignatedChannelType.Starboard);
+ .HasDesignatedChannelForType(channel.GuildId, DesignatedChannelType.Starboard);
if (isIgnoredFromStarboard || !starboardExists)
{
diff --git a/src/Modix.Data/Models/Core/ConfigurationActionEntity.cs b/src/Modix.Data/Models/Core/ConfigurationActionEntity.cs
index a613f47f6..c43908efa 100644
--- a/src/Modix.Data/Models/Core/ConfigurationActionEntity.cs
+++ b/src/Modix.Data/Models/Core/ConfigurationActionEntity.cs
@@ -5,106 +5,66 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
-namespace Modix.Data.Models.Core
+namespace Modix.Data.Models.Core;
+
+[Table("ConfigurationActions")]
+public class ConfigurationActionEntity
{
- ///
- /// Describes an action that was performed, that somehow changed the application's configuration.
- ///
- [Table("ConfigurationActions")]
- public class ConfigurationActionEntity
- {
- ///
- /// A unique identifier for this configuration action.
- ///
- [Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
-
- ///
- /// The snowflake ID, within the Discord API, of the guild to which this configuration action applies.
- ///
- [Required]
- public ulong GuildId { get; set; }
-
- ///
- /// The type of configuration action that was performed.
- ///
- [Required]
- public ConfigurationActionType Type { get; set; }
-
- ///
- /// A timestamp indicating when this configuration action was performed.
- ///
- [Required]
- public DateTimeOffset Created { get; set; }
-
- ///
- /// The value of .
- ///
- [Required]
- public ulong CreatedById { get; set; }
-
- ///
- /// The Discord user that performed this action.
- ///
- [Required]
- public virtual GuildUserEntity CreatedBy { get; set; } = null!;
-
- ///
- /// The value (if any) of .
- ///
- [ForeignKey(nameof(ClaimMapping))]
- public long? ClaimMappingId { get; set; }
-
- ///
- /// The claim mapping that was affected by this action, if any.
- ///
- public ClaimMappingEntity? ClaimMapping { get; set; }
-
- ///
- /// The value (if any) of .
- ///
- [ForeignKey(nameof(DesignatedChannelMapping))]
- public long? DesignatedChannelMappingId { get; set; }
-
- ///
- /// The designated channel mapping that was affected by this action, if any.
- ///
- public DesignatedChannelMappingEntity? DesignatedChannelMapping { get; set; }
-
- ///
- /// The value (if any) of .
- ///
- [ForeignKey(nameof(DesignatedRoleMapping))]
- public long? DesignatedRoleMappingId { get; set; }
-
- ///
- /// The designated role mapping that was affected by this action, if any.
- ///
- public DesignatedRoleMappingEntity? DesignatedRoleMapping { get; set; }
- }
+ [Key, Required, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public long Id { get; set; }
+
+ [Required]
+ public ulong GuildId { get; set; }
+
+ [Required]
+ public ConfigurationActionType Type { get; set; }
+
+ [Required]
+ public DateTimeOffset Created { get; set; }
+
+ [Required]
+ public ulong CreatedById { get; set; }
+
+ [Required]
+ public virtual GuildUserEntity CreatedBy { get; set; } = null!;
- public class ConfigurationActionEntityConfigurator
- : IEntityTypeConfiguration
+ [ForeignKey(nameof(ClaimMapping))]
+ public long? ClaimMappingId { get; set; }
+
+ public ClaimMappingEntity? ClaimMapping { get; set; }
+
+ [ForeignKey(nameof(DesignatedChannelMapping))]
+ public long? DesignatedChannelMappingId { get; set; }
+
+ public DesignatedChannelMappingEntity? DesignatedChannelMapping { get; set; }
+
+ [ForeignKey(nameof(DesignatedRoleMapping))]
+ public long? DesignatedRoleMappingId { get; set; }
+
+ public DesignatedRoleMappingEntity? DesignatedRoleMapping { get; set; }
+}
+
+public class ConfigurationActionEntityConfigurator
+ : IEntityTypeConfiguration
+{
+ public void Configure(
+ EntityTypeBuilder entityTypeBuilder)
{
- public void Configure(
- EntityTypeBuilder entityTypeBuilder)
- {
- entityTypeBuilder
- .Property(x => x.Type)
- .HasConversion();
-
- entityTypeBuilder
- .Property(x => x.GuildId)
- .HasConversion();
-
- entityTypeBuilder
- .Property(x => x.CreatedById)
- .HasConversion();
-
- entityTypeBuilder
- .HasOne(x => x.CreatedBy)
- .WithMany()
- .HasForeignKey(x => new { x.GuildId, x.CreatedById });
- }
+ entityTypeBuilder
+ .Property(x => x.Type)
+ .HasConversion();
+
+ entityTypeBuilder
+ .Property(x => x.GuildId)
+ .HasConversion();
+
+ entityTypeBuilder
+ .Property(x => x.CreatedById)
+ .HasConversion();
+
+ entityTypeBuilder
+ .HasOne(x => x.CreatedBy)
+ .WithMany()
+ .HasForeignKey(x => new { x.GuildId, x.CreatedById });
}
}
diff --git a/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs b/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs
index 2d9ad7dec..4a48f3749 100644
--- a/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs
+++ b/src/Modix.Data/Models/Core/DesignatedChannelMappingCreationData.cs
@@ -26,20 +26,5 @@ public class DesignatedChannelMappingCreationData
/// See .
///
public ulong CreatedById { get; set; }
-
- internal DesignatedChannelMappingEntity ToEntity()
- => new DesignatedChannelMappingEntity()
- {
- GuildId = GuildId,
- ChannelId = ChannelId,
- Type = Type,
- CreateAction = new ConfigurationActionEntity()
- {
- GuildId = GuildId,
- Type = ConfigurationActionType.DesignatedChannelMappingCreated,
- Created = DateTimeOffset.UtcNow,
- CreatedById = CreatedById
- }
- };
}
}
diff --git a/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs b/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs
index 65b49e42a..7c6bc6957 100644
--- a/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs
+++ b/src/Modix.Data/Models/Core/DesignatedChannelMappingEntity.cs
@@ -4,59 +4,58 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
-namespace Modix.Data.Models.Core
+namespace Modix.Data.Models.Core;
+
+[Table("DesignatedChannelMappings")]
+public class DesignatedChannelMappingEntity
{
- [Table("DesignatedChannelMappings")]
- public class DesignatedChannelMappingEntity
- {
- [Required, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
- public long Id { get; set; }
+ [Required, Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
+ public long Id { get; set; }
- public ulong GuildId { get; set; }
+ public ulong GuildId { get; set; }
- [ForeignKey(nameof(Channel))]
- public ulong ChannelId { get; set; }
+ [ForeignKey(nameof(Channel))]
+ public ulong ChannelId { get; set; }
- public virtual GuildChannelEntity Channel { get; set; } = null!;
+ public virtual GuildChannelEntity Channel { get; set; } = null!;
- public DesignatedChannelType Type { get; set; }
+ public DesignatedChannelType Type { get; set; }
- public long CreateActionId { get; set; }
+ public long CreateActionId { get; set; }
- public virtual ConfigurationActionEntity CreateAction { get; set; } = null!;
+ public virtual ConfigurationActionEntity CreateAction { get; set; } = null!;
- public long? DeleteActionId { get; set; }
+ public long? DeleteActionId { get; set; }
- public virtual ConfigurationActionEntity? DeleteAction { get; set; }
- }
+ public virtual ConfigurationActionEntity? DeleteAction { get; set; }
+}
- public class DesignatedChannelMappingEntityConfiguration
- : IEntityTypeConfiguration
+public class DesignatedChannelMappingEntityConfiguration
+ : IEntityTypeConfiguration
+{
+ public void Configure(
+ EntityTypeBuilder entityTypeBuilder)
{
- public void Configure(
- EntityTypeBuilder entityTypeBuilder)
- {
- entityTypeBuilder
- .Property(x => x.Type)
- .HasConversion();
-
- entityTypeBuilder
- .Property(x => x.GuildId)
- .HasConversion();
-
- entityTypeBuilder
- .Property(x => x.ChannelId)
- .HasConversion();
-
- entityTypeBuilder
- .HasOne(x => x.CreateAction)
- .WithOne()
- .HasForeignKey(x => x.CreateActionId);
-
- entityTypeBuilder
- .HasOne(x => x.DeleteAction)
- .WithOne()
- .HasForeignKey(x => x.DeleteActionId);
- }
+ entityTypeBuilder
+ .Property(x => x.Type)
+ .HasConversion();
+
+ entityTypeBuilder
+ .Property(x => x.GuildId)
+ .HasConversion();
+
+ entityTypeBuilder
+ .Property(x => x.ChannelId)
+ .HasConversion();
+
+ entityTypeBuilder
+ .HasOne(x => x.CreateAction)
+ .WithOne()
+ .HasForeignKey(x => x.CreateActionId);
+
+ entityTypeBuilder
+ .HasOne(x => x.DeleteAction)
+ .WithOne()
+ .HasForeignKey(x => x.DeleteActionId);
}
}
diff --git a/src/Modix.Data/Repositories/DesignatedChannelMappingRepository.cs b/src/Modix.Data/Repositories/DesignatedChannelMappingRepository.cs
deleted file mode 100644
index e5ad3d20b..000000000
--- a/src/Modix.Data/Repositories/DesignatedChannelMappingRepository.cs
+++ /dev/null
@@ -1,207 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-
-using Microsoft.EntityFrameworkCore;
-
-using Modix.Data.ExpandableQueries;
-using Modix.Data.Models.Core;
-
-namespace Modix.Data.Repositories
-{
- ///
- /// Describes a repository for managing entities, within an underlying data storage provider.
- ///
- public interface IDesignatedChannelMappingRepository
- {
- ///
- /// Begins a new transaction to create mappings within the repository.
- ///
- ///
- /// A that will complete, with the requested transaction object,
- /// when no other create transactions are active upon the repository.
- ///
- Task BeginCreateTransactionAsync();
-
- ///
- /// Begins a new transaction to delete mappings within the repository.
- ///
- ///
- /// A that will complete, with the requested transaction object,
- /// when no other delete transactions are active upon the repository.
- ///
- Task BeginDeleteTransactionAsync();
-
- ///
- /// Creates a new mapping withn the repository.
- /// This method must not be called outside of the scope of an returned by .
- ///
- /// The data for the mapping to be created.
- /// Throws for .
- ///
- /// A which will complete when the operation is complete,
- /// containing the auto-generated value assigned to the new mapping.
- ///
- Task CreateAsync(DesignatedChannelMappingCreationData data);
-
- ///
- /// Checks whether any mappings exist within the repository, according to an arbitrary set of criteria.
- ///
- /// A set of criteria defining the mappings to check for.
- /// A token that may be used to cancel the operation.
- ///
- /// A that will complete when the operation has completed,
- /// containing a flag indicating whether any matching mappings were found.
- ///
- Task AnyAsync(
- DesignatedChannelMappingSearchCriteria criteria,
- CancellationToken cancellationToken);
-
- ///
- /// Searches the repository for mapped values, based on an arbitrary set of criteria
- ///
- /// The criteria for selecting ID values to be returned.
- /// A which will complete when the matching values have been retrieved.
- Task> SearchChannelIdsAsync(DesignatedChannelMappingSearchCriteria searchCriteria);
-
- ///
- /// Searches the repository for mappings, based on an arbitrary set of criteria.
- ///
- /// The criteria for selecting records to be returned.
- /// A which will complete when the matching records have been retrieved.
- Task> SearchBriefsAsync(DesignatedChannelMappingSearchCriteria searchCriteria);
-
- ///
- /// Marks mappings within the repository as deleted, based on a given set of search criteria.
- /// This method must not be called outside of the scope of an returned by .
- ///
- /// A set of criteria defining the mappings to be deleted.
- /// The value of the user that is deleting the mapping.
- ///
- /// A which will complete when the operation is complete,
- /// containing the total number of mappings that were deleted, based on the given criteria.
- ///
- Task DeleteAsync(DesignatedChannelMappingSearchCriteria criteria, ulong deletedById);
-
- ///
- /// Marks an existing mapping as deleted, based on its ID.
- /// This method must not be called outside of the scope of an returned by .
- ///
- /// The value of the mapping to be deleted.
- /// The value of the user that is deleting the mapping.
- /// A which will complete when the operation is complete,
- /// containing a flag indicating whether the operation was successful (I.E. whether the specified mapping could be found).
- ///
- Task TryDeleteAsync(long mappingId, ulong deletedById);
- }
-
- ///
- public class DesignatedChannelMappingRepository : RepositoryBase, IDesignatedChannelMappingRepository
- {
- ///
- /// Creates a new .
- /// See for details.
- ///
- public DesignatedChannelMappingRepository(ModixContext modixContext)
- : base(modixContext) { }
-
- ///
- public Task BeginCreateTransactionAsync()
- => _createTransactionFactory.BeginTransactionAsync(ModixContext.Database);
-
- ///
- public Task BeginDeleteTransactionAsync()
- => _deleteTransactionFactory.BeginTransactionAsync(ModixContext.Database);
-
- ///
- public async Task CreateAsync(DesignatedChannelMappingCreationData data)
- {
- if (data == null)
- throw new ArgumentNullException(nameof(data));
-
- var entity = data.ToEntity();
-
- await ModixContext.Set().AddAsync(entity);
- await ModixContext.SaveChangesAsync();
-
- entity.CreateAction.DesignatedChannelMappingId = entity.Id;
- await ModixContext.SaveChangesAsync();
-
- return entity.Id;
- }
-
- ///
- public Task AnyAsync(
- DesignatedChannelMappingSearchCriteria criteria,
- CancellationToken cancellationToken)
- => ModixContext.Set().AsNoTracking()
- .FilterBy(criteria)
- .AnyAsync(cancellationToken);
-
- ///
- public async Task> SearchChannelIdsAsync(DesignatedChannelMappingSearchCriteria searchCriteria)
- => await ModixContext.Set().AsNoTracking()
- .FilterBy(searchCriteria)
- .Select(x => x.ChannelId)
- .ToArrayAsync();
-
- ///
- public async Task> SearchBriefsAsync(DesignatedChannelMappingSearchCriteria searchCriteria)
- => await ModixContext.Set().AsNoTracking()
- .FilterBy(searchCriteria)
- .AsExpandable()
- .Select(DesignatedChannelMappingBrief.FromEntityProjection)
- .ToArrayAsync();
-
- ///
- public async Task DeleteAsync(DesignatedChannelMappingSearchCriteria criteria, ulong deletedById)
- {
- var entities = await ModixContext.Set()
- .Where(x => x.DeleteActionId == null)
- .FilterBy(criteria)
- .ToArrayAsync();
-
- foreach (var entity in entities)
- DoEntityDelete(entity, deletedById);
-
- await ModixContext.SaveChangesAsync();
-
- return entities.Length;
- }
-
- ///
- public async Task TryDeleteAsync(long mappingId, ulong deletedById)
- {
- var entity = await ModixContext.Set()
- .Where(x => x.Id == mappingId)
- .FirstOrDefaultAsync();
-
- if ((entity == null) || (entity.DeleteActionId != null))
- return false;
-
- DoEntityDelete(entity, deletedById);
-
- await ModixContext.SaveChangesAsync();
-
- return true;
- }
-
- private static readonly RepositoryTransactionFactory _createTransactionFactory
- = new RepositoryTransactionFactory();
-
- private static readonly RepositoryTransactionFactory _deleteTransactionFactory
- = new RepositoryTransactionFactory();
-
- private void DoEntityDelete(DesignatedChannelMappingEntity entity, ulong deletedById)
- => entity.DeleteAction = new ConfigurationActionEntity()
- {
- Type = ConfigurationActionType.DesignatedChannelMappingDeleted,
- Created = DateTimeOffset.UtcNow,
- CreatedById = deletedById,
- DesignatedChannelMappingId = entity.Id,
- GuildId = entity.GuildId
- };
- }
-}
diff --git a/src/Modix.Services/Core/CoreSetup.cs b/src/Modix.Services/Core/CoreSetup.cs
index 58d465dc3..dfa3f044f 100644
--- a/src/Modix.Services/Core/CoreSetup.cs
+++ b/src/Modix.Services/Core/CoreSetup.cs
@@ -35,11 +35,10 @@ public static IServiceCollection AddModixCore(this IServiceCollection services)
.AddScoped()
.AddScoped()
.AddScoped()
- .AddScoped()
+ .AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
- .AddScoped()
.AddScoped()
.AddScoped()
.AddScoped();
diff --git a/src/Modix.Services/Core/DesignatedChannelService.cs b/src/Modix.Services/Core/DesignatedChannelService.cs
deleted file mode 100644
index cc7f62129..000000000
--- a/src/Modix.Services/Core/DesignatedChannelService.cs
+++ /dev/null
@@ -1,265 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using Discord;
-using Modix.Data.Models.Core;
-using Modix.Data.Repositories;
-using Serilog;
-
-namespace Modix.Services.Core
-{
- ///
- /// Provides methods for managing and interacting with Discord channels, designated for use within the application.
- ///
- public interface IDesignatedChannelService
- {
- ///
- /// Assigns a channel to a given designation, for a given guild.
- ///
- /// The guild where the exists.
- /// The channel to be assigned.
- /// The type of designation to be assigned.
- /// A that will complete when the operation has completed.
- Task AddDesignatedChannelAsync(IGuild guild, IMessageChannel channel, DesignatedChannelType type);
-
- ///
- /// Unassigns a channel's previously given designation, for a given guild.
- ///
- /// The guild where the exists.
- /// The channel to be unassigned.
- /// The type of designation to be unassigned.
- /// A that will complete when the operation has completed, with the number of records deleted.
- Task RemoveDesignatedChannelAsync(IGuild guild, IMessageChannel channel, DesignatedChannelType type);
-
- ///
- /// Removes a channel designation by ID.
- ///
- /// The ID of the designation to be removed.
- /// A that will complete when the operation has completed, with the number of records deleted.
- Task RemoveDesignatedChannelByIdAsync(long designationId);
-
- ///
- /// Checks whether any designated channels exist, for an arbitrary set of criteria.
- ///
- /// The Discord snowflake ID of the guild whose designated channels are to be checked.
- /// The type of designated channels to check for.
- ///
- /// A that will complete when the operation has completed,
- /// containing a flag indicating whether any matching channel designations exist.
- ///
- Task AnyDesignatedChannelAsync(ulong guildId, DesignatedChannelType type);
-
- ///
- /// Retrieves the Discord snowflake ID values of the channels assigned to a given designation, for a given guild.
- ///
- /// The Discord snowflake ID of the guild for which channel designations are to be retrieved.
- /// The type of designation for which channels are to be retrieved.
- ///
- /// A that will complete when the operation has compelted,
- /// containing the requested channel ID values.
- ///
- Task> GetDesignatedChannelIdsAsync(ulong guildId, DesignatedChannelType type);
-
- ///
- /// Retrieves the s assigned to the given designation, for a given guild.
- ///
- /// The guild to retrieve designations for
- /// The designation to retrieve assigned channels for
- /// A that will complete when all s have been retrieved.
- Task> GetDesignatedChannelsAsync(IGuild guild, DesignatedChannelType type);
-
- ///
- /// Retrieves a collection of s associated with the given guild.
- ///
- /// The ID of the guild to retrieve designations for
- /// A that will complete when all s have been retrieved.
- Task> GetDesignatedChannelsAsync(ulong guildId);
-
- ///
- /// Checks if the given channel has the given designation
- ///
- /// The where the channel is located
- /// The to check the designation for
- /// The to check for
- /// A token that may be used to cancel the operation.
- ///
- Task ChannelHasDesignationAsync(
- ulong guildId,
- ulong channelId,
- DesignatedChannelType designation,
- CancellationToken cancellationToken);
-
- ///
- /// Sends the given message (and embed) to the s assigned to the given designation, for a given guild.
- ///
- /// The to send the message to
- /// The of the channels to send the messages to
- /// The text content of the message
- /// An optional to attach to the message
- /// A that, when completed, results in a collection of the messages that were sent.
- Task> SendToDesignatedChannelsAsync(IGuild guild, DesignatedChannelType designation, string content, Embed embed = null);
- }
-
- public class DesignatedChannelService : IDesignatedChannelService
- {
- internal protected IDesignatedChannelMappingRepository DesignatedChannelMappingRepository { get; }
- internal protected IAuthorizationService AuthorizationService { get; }
-
- public DesignatedChannelService(IDesignatedChannelMappingRepository designatedChannelMappingRepository, IAuthorizationService authorizationService)
- {
- DesignatedChannelMappingRepository = designatedChannelMappingRepository;
- AuthorizationService = authorizationService;
- }
-
- ///
- public async Task AddDesignatedChannelAsync(IGuild guild, IMessageChannel logChannel, DesignatedChannelType type)
- {
- AuthorizationService.RequireAuthenticatedUser();
- AuthorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingCreate);
-
- using (var transaction = await DesignatedChannelMappingRepository.BeginCreateTransactionAsync())
- {
- if (await DesignatedChannelMappingRepository.AnyAsync(new DesignatedChannelMappingSearchCriteria()
- {
- GuildId = guild.Id,
- ChannelId = logChannel.Id,
- IsDeleted = false,
- Type = type
- }, default))
- {
- throw new InvalidOperationException($"{logChannel.Name} in {guild.Name} is already assigned to {type}");
- }
-
- var id = await DesignatedChannelMappingRepository.CreateAsync(new DesignatedChannelMappingCreationData()
- {
- GuildId = guild.Id,
- ChannelId = logChannel.Id,
- CreatedById = AuthorizationService.CurrentUserId.Value,
- Type = type
- });
-
- transaction.Commit();
-
- return id;
- }
- }
-
- ///
- public async Task RemoveDesignatedChannelAsync(IGuild guild, IMessageChannel logChannel, DesignatedChannelType type)
- {
- AuthorizationService.RequireAuthenticatedUser();
- AuthorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingDelete);
-
- using (var transaction = await DesignatedChannelMappingRepository.BeginDeleteTransactionAsync())
- {
- var deletedCount = await DesignatedChannelMappingRepository.DeleteAsync(new DesignatedChannelMappingSearchCriteria()
- {
- GuildId = guild.Id,
- ChannelId = logChannel.Id,
- IsDeleted = false,
- Type = type
- }, AuthorizationService.CurrentUserId.Value);
-
- if (deletedCount == 0)
- throw new InvalidOperationException($"{logChannel.Name} in {guild.Name} is not assigned to {type}");
-
- transaction.Commit();
- return deletedCount;
- }
- }
-
- ///
- public async Task RemoveDesignatedChannelByIdAsync(long designationId)
- {
- AuthorizationService.RequireAuthenticatedUser();
- AuthorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingDelete);
-
- using (var transaction = await DesignatedChannelMappingRepository.BeginDeleteTransactionAsync())
- {
- var deletedCount = await DesignatedChannelMappingRepository.DeleteAsync(new DesignatedChannelMappingSearchCriteria()
- {
- Id = designationId,
- IsDeleted = false
- }, AuthorizationService.CurrentUserId.Value);
-
- if (deletedCount == 0)
- throw new InvalidOperationException($"No designations with id {designationId} found.");
-
- transaction.Commit();
- return deletedCount;
- }
- }
-
- ///
- public Task AnyDesignatedChannelAsync(ulong guildId, DesignatedChannelType type)
- => DesignatedChannelMappingRepository.AnyAsync(new DesignatedChannelMappingSearchCriteria()
- {
- GuildId = guildId,
- Type = type,
- IsDeleted = false
- }, default);
-
- ///
- public Task> GetDesignatedChannelIdsAsync(ulong guildId, DesignatedChannelType type)
- => DesignatedChannelMappingRepository.SearchChannelIdsAsync(new DesignatedChannelMappingSearchCriteria()
- {
- GuildId = guildId,
- Type = type,
- IsDeleted = false
- });
-
- ///
- public async Task> GetDesignatedChannelsAsync(IGuild guild, DesignatedChannelType type)
- {
- var channelIds = await GetDesignatedChannelIdsAsync(guild.Id, type);
-
- if (!channelIds.Any())
- throw new InvalidOperationException($"{guild.Name} has no channels assigned to {type}");
-
- return (await Task.WhenAll(channelIds.Select(d => guild.GetChannelAsync(d))))
- .OfType()
- .ToArray();
- }
-
- ///
- public async Task> SendToDesignatedChannelsAsync(IGuild guild, DesignatedChannelType designation, string text, Embed embed = null)
- {
- var channels = await GetDesignatedChannelsAsync(guild, designation);
-
- if (!channels.Any())
- {
- Log.Warning("Warning: Tried to send to channels assigned to designation {designation}, but none were assigned.", new { designation });
- }
-
- return await Task.WhenAll(channels.Select(channel => channel.SendMessageAsync(text, false, embed)));
- }
-
- ///
- public Task> GetDesignatedChannelsAsync(ulong guildId)
- {
- AuthorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingRead);
-
- return DesignatedChannelMappingRepository.SearchBriefsAsync(new DesignatedChannelMappingSearchCriteria()
- {
- GuildId = guildId,
- IsDeleted = false
- });
- }
-
- ///
- public Task ChannelHasDesignationAsync(
- ulong guildId,
- ulong channelId,
- DesignatedChannelType designation,
- CancellationToken cancellationToken)
- => DesignatedChannelMappingRepository.AnyAsync(new DesignatedChannelMappingSearchCriteria()
- {
- GuildId = guildId,
- Type = designation,
- ChannelId = channelId,
- IsDeleted = false
- }, cancellationToken);
- }
-}
diff --git a/src/Modix.Services/DesignatedChannelService.cs b/src/Modix.Services/DesignatedChannelService.cs
new file mode 100644
index 000000000..0343d33d8
--- /dev/null
+++ b/src/Modix.Services/DesignatedChannelService.cs
@@ -0,0 +1,172 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Discord;
+using Microsoft.EntityFrameworkCore;
+using Modix.Data;
+using Modix.Data.Models.Core;
+using Modix.Services.Core;
+
+namespace Modix.Services;
+
+public class DesignatedChannelService(
+ ModixContext db,
+ IAuthorizationService authorizationService)
+{
+ public async Task AddDesignatedChannel(IGuild guild, IMessageChannel logChannel,
+ DesignatedChannelType type)
+ {
+ authorizationService.RequireAuthenticatedUser();
+ authorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingCreate);
+
+ var designationId = await db
+ .Set()
+ .Where(x => x.GuildId == guild.Id)
+ .Where(x => x.ChannelId == logChannel.Id)
+ .Where(x => x.Type == type)
+ .Where(x => x.DeleteActionId == null)
+ .Select(x => (long?)x.Id)
+ .SingleOrDefaultAsync();
+
+ if (designationId is not null)
+ {
+ return designationId.Value;
+ }
+
+ var newEntity = new DesignatedChannelMappingEntity
+ {
+ GuildId = guild.Id,
+ ChannelId = logChannel.Id,
+ Type = type,
+ CreateAction = new ConfigurationActionEntity
+ {
+ GuildId = guild.Id,
+ Type = ConfigurationActionType.DesignatedChannelMappingCreated,
+ Created = DateTimeOffset.UtcNow,
+ CreatedById = authorizationService.CurrentUserId!.Value,
+ }
+ };
+
+ db.Add(newEntity);
+ await db.SaveChangesAsync();
+
+ return newEntity.Id;
+ }
+
+ public async Task RemoveDesignatedChannel(IGuild guild, IMessageChannel logChannel, DesignatedChannelType type)
+ {
+ var designationId = await db
+ .Set()
+ .Where(x => x.GuildId == guild.Id)
+ .Where(x => x.ChannelId == logChannel.Id)
+ .Where(x => x.Type == type)
+ .Where(x => x.DeleteActionId == null)
+ .Select(x => (long?)x.Id)
+ .SingleOrDefaultAsync();
+
+ if (designationId is null)
+ {
+ return;
+ }
+
+ await RemoveDesignatedChannelById(designationId.Value);
+ }
+
+ public async Task RemoveDesignatedChannelById(long designationId)
+ {
+ authorizationService.RequireAuthenticatedUser();
+ authorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingDelete);
+
+ var designation = await db
+ .Set()
+ .Where(x => x.Id == designationId)
+ .SingleAsync();
+
+ var deleteAction = new ConfigurationActionEntity
+ {
+ Type = ConfigurationActionType.DesignatedChannelMappingDeleted,
+ Created = DateTimeOffset.UtcNow,
+ CreatedById = authorizationService.CurrentUserId!.Value,
+ DesignatedChannelMappingId = designation.Id,
+ GuildId = designation.GuildId,
+ };
+
+ db.Add(deleteAction);
+ await db.SaveChangesAsync();
+ }
+
+ public async Task HasDesignatedChannelForType(ulong guildId, DesignatedChannelType type)
+ {
+ return await db
+ .Set()
+ .Where(x => x.GuildId == guildId)
+ .Where(x => x.Type == type)
+ .Where(x => x.DeleteActionId == null)
+ .AnyAsync();
+ }
+
+ public async Task> GetDesignatedChannelIds(ulong guildId, DesignatedChannelType type)
+ {
+ return await db
+ .Set()
+ .Where(x => x.GuildId == guildId)
+ .Where(x => x.Type == type)
+ .Select(x => x.ChannelId)
+ .ToListAsync();
+ }
+
+ public async Task> GetDesignatedChannels(IGuild guild,
+ DesignatedChannelType type)
+ {
+ var channelIds = await GetDesignatedChannelIds(guild.Id, type);
+
+ if (!channelIds.Any())
+ {
+ return [];
+ }
+
+ var channels = await Task.WhenAll(channelIds.Select(d => guild.GetChannelAsync(d)));
+
+ return channels
+ .OfType()
+ .ToArray();
+ }
+
+ public async Task> GetDesignatedChannels(ulong guildId)
+ {
+ authorizationService.RequireClaims(AuthorizationClaim.DesignatedChannelMappingRead);
+
+ return await db
+ .Set()
+ .Where(x => x.GuildId == guildId)
+ .Where(x => x.DeleteActionId == null)
+ .Select(x => new DesignatedChannelMappingBrief
+ {
+ Id = x.Id,
+ Channel = new GuildChannelBrief
+ {
+ Id = x.ChannelId,
+ Name = x.Channel.Name,
+ ParentChannelId = x.Channel.ParentChannelId,
+ },
+ Type = x.Type,
+ }).ToListAsync();
+ }
+
+ public async Task ChannelHasDesignation(
+ ulong guildId,
+ ulong channelId,
+ DesignatedChannelType type,
+ CancellationToken cancellationToken)
+ {
+ return await db
+ .Set()
+ .Where(x => x.GuildId == guildId)
+ .Where(x => x.ChannelId == channelId)
+ .Where(x => x.Type == type)
+ .Where(x => x.DeleteActionId == null)
+ .AnyAsync(cancellationToken: cancellationToken);
+ }
+}
diff --git a/src/Modix.Services/DiscordRelayService.cs b/src/Modix.Services/DiscordRelayService.cs
new file mode 100644
index 000000000..a4b493354
--- /dev/null
+++ b/src/Modix.Services/DiscordRelayService.cs
@@ -0,0 +1,20 @@
+using System.Threading.Tasks;
+using Discord;
+using MediatR;
+
+namespace Modix.Services;
+
+public class DiscordRelayService(IMediator mediator)
+{
+ public async Task SendMessageToChannel(ulong channelId, string message, Embed embed = null)
+ {
+ return await mediator.Send(new SendMessageInDiscordRequest(channelId, message, embed));
+ }
+}
+
+public class SendMessageInDiscordRequest(ulong channelId, string message, Embed embed) : IRequest
+{
+ public ulong ChannelId { get; } = channelId;
+ public string Message { get; } = message;
+ public Embed Embed { get; } = embed;
+}
diff --git a/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs b/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs
index 664255971..6ad69105a 100644
--- a/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs
+++ b/src/Modix.Services/MessageLogging/MessageLoggingBehavior.cs
@@ -24,7 +24,7 @@ public class MessageLoggingBehavior
INotificationHandler
{
public MessageLoggingBehavior(
- IDesignatedChannelService designatedChannelService,
+ DesignatedChannelService designatedChannelService,
DiscordSocketClient discordSocketClient,
ILogger logger)
{
@@ -175,7 +175,7 @@ private async Task TryLogAsync(
return;
}
- var channelIsUnmoderated = await _designatedChannelService.ChannelHasDesignationAsync(
+ var channelIsUnmoderated = await _designatedChannelService.ChannelHasDesignation(
guild.Id,
channel.Id,
DesignatedChannelType.Unmoderated,
@@ -187,7 +187,7 @@ private async Task TryLogAsync(
}
MessageLoggingLogMessages.ModeratedChannelIdentified(_logger);
- var messageLogChannels = await _designatedChannelService.GetDesignatedChannelsAsync(
+ var messageLogChannels = await _designatedChannelService.GetDesignatedChannels(
guild,
DesignatedChannelType.MessageLog);
if (messageLogChannels.Count == 0)
@@ -207,7 +207,7 @@ private async Task TryLogAsync(
}
}
- private readonly IDesignatedChannelService _designatedChannelService;
+ private readonly DesignatedChannelService _designatedChannelService;
private readonly DiscordSocketClient _discordSocketClient;
private readonly ILogger _logger;
}
diff --git a/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs b/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs
index bd0438a78..21ffe8783 100644
--- a/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs
+++ b/src/Modix.Services/Moderation/AttachmentBlacklistBehavior.cs
@@ -95,7 +95,7 @@ public class AttachmentBlacklistBehavior
};
public AttachmentBlacklistBehavior(
- IDesignatedChannelService designatedChannelService,
+ DesignatedChannelService designatedChannelService,
DiscordSocketClient discordSocketClient,
ILogger logger,
IModerationService moderationService)
@@ -136,7 +136,7 @@ public async Task HandleNotificationAsync(
}
AttachmentBlacklistLogMessages.ChannelModerationStatusFetching(_logger);
- var channelIsUnmoderated = await _designatedChannelService.ChannelHasDesignationAsync(
+ var channelIsUnmoderated = await _designatedChannelService.ChannelHasDesignation(
guild.Id,
channel.Id,
DesignatedChannelType.Unmoderated,
@@ -180,7 +180,7 @@ await message.Channel.SendMessageAsync(
AttachmentBlacklistLogMessages.ReplySent(_logger);
}
- private readonly IDesignatedChannelService _designatedChannelService;
+ private readonly DesignatedChannelService _designatedChannelService;
private readonly DiscordSocketClient _discordSocketClient;
private readonly ILogger _logger;
private readonly IModerationService _moderationService;
diff --git a/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs b/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs
index 3eeff67e8..70129c2fc 100644
--- a/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs
+++ b/src/Modix.Services/Moderation/MessageContentCheckBehaviour.cs
@@ -18,14 +18,14 @@ public class MessageContentCheckBehaviour :
INotificationHandler,
INotificationHandler
{
- private readonly IDesignatedChannelService _designatedChannelService;
+ private readonly DesignatedChannelService _designatedChannelService;
private readonly IAuthorizationService _authorizationService;
private readonly IModerationService _moderationService;
private readonly IMessageContentPatternService _messageContentPatternService;
private readonly DiscordSocketClient _discordSocketClient;
public MessageContentCheckBehaviour(
- IDesignatedChannelService designatedChannelService,
+ DesignatedChannelService designatedChannelService,
DiscordSocketClient discordSocketClient,
IAuthorizationService authorizationService,
IModerationService moderationService, IMessageContentPatternService messageContentPatternService)
@@ -91,7 +91,7 @@ private async Task TryCheckMessageAsync(IMessage message)
return;
}
- if (await _designatedChannelService.ChannelHasDesignationAsync(channel.Guild.Id,
+ if (await _designatedChannelService.ChannelHasDesignation(channel.Guild.Id,
channel.Id, DesignatedChannelType.Unmoderated, default))
{
return;
diff --git a/src/Modix.Services/Moderation/ModerationService.cs b/src/Modix.Services/Moderation/ModerationService.cs
index 93efa6a26..006f712fa 100644
--- a/src/Modix.Services/Moderation/ModerationService.cs
+++ b/src/Modix.Services/Moderation/ModerationService.cs
@@ -82,7 +82,7 @@ public class ModerationService : IModerationService
private readonly IDeletedMessageRepository _deletedMessageRepository;
private readonly IDeletedMessageBatchRepository _deletedMessageBatchRepository;
private readonly IRoleService _roleService;
- private readonly IDesignatedChannelService _designatedChannelService;
+ private readonly DesignatedChannelService _designatedChannelService;
// TODO: Push this to a bot-wide config? Or maybe on a per-guild basis, but with a bot-wide default, that's pulled from config?
private const string MuteRoleName = "MODiX_Moderation_Mute";
@@ -100,7 +100,7 @@ public ModerationService(
IDeletedMessageRepository deletedMessageRepository,
IDeletedMessageBatchRepository deletedMessageBatchRepository,
IRoleService roleService,
- IDesignatedChannelService designatedChannelService)
+ DesignatedChannelService designatedChannelService)
{
_discordClient = discordClient;
_authorizationService = authorizationService;
@@ -128,7 +128,7 @@ private async Task SetUpMuteRole(IGuild guild)
var muteRole = await GetOrCreateDesignatedMuteRoleAsync(guild, _authorizationService.CurrentUserId!.Value);
var unmoderatedChannels =
- await _designatedChannelService.GetDesignatedChannelIdsAsync(guild.Id,
+ await _designatedChannelService.GetDesignatedChannelIds(guild.Id,
DesignatedChannelType.Unmoderated);
var nonCategoryChannels =
@@ -169,7 +169,7 @@ public async Task AutoConfigureChannelAsync(IChannel channel)
if (channel is IGuildChannel guildChannel)
{
- var isUnmoderated = await _designatedChannelService.ChannelHasDesignationAsync(guildChannel.Guild.Id,
+ var isUnmoderated = await _designatedChannelService.ChannelHasDesignation(guildChannel.Guild.Id,
channel.Id, DesignatedChannelType.Unmoderated, default);
if (isUnmoderated)
diff --git a/src/Modix.Services/Starboard/StarboardService.cs b/src/Modix.Services/Starboard/StarboardService.cs
index 986c43036..1bb97abf0 100644
--- a/src/Modix.Services/Starboard/StarboardService.cs
+++ b/src/Modix.Services/Starboard/StarboardService.cs
@@ -80,7 +80,7 @@ public interface IStarboardService
///
public class StarboardService : IStarboardService
{
- private readonly IDesignatedChannelService _designatedChannelService;
+ private readonly DesignatedChannelService _designatedChannelService;
private readonly IMessageRepository _messageRepository;
private static readonly IReadOnlyDictionary _emojis = new Dictionary
{
@@ -91,7 +91,7 @@ public class StarboardService : IStarboardService
}.OrderByDescending(k => k.Key).ToDictionary(k => k.Key, k => k.Value);
public StarboardService(
- IDesignatedChannelService designatedChannelService,
+ DesignatedChannelService designatedChannelService,
IMessageRepository messageRepository)
{
_designatedChannelService = designatedChannelService;
@@ -115,7 +115,7 @@ private async Task GetStarboardEntry(IGuild guild, IMessage messag
private async Task GetStarboardChannel(IGuild guild)
{
var starboardChannels = await _designatedChannelService
- .GetDesignatedChannelsAsync(guild, DesignatedChannelType.Starboard);
+ .GetDesignatedChannels(guild, DesignatedChannelType.Starboard);
return starboardChannels.First() as ITextChannel;
}
diff --git a/src/Modix.Web/Components/Configuration/Channels.razor b/src/Modix.Web/Components/Configuration/Channels.razor
index 2a57d855b..f9521657a 100644
--- a/src/Modix.Web/Components/Configuration/Channels.razor
+++ b/src/Modix.Web/Components/Configuration/Channels.razor
@@ -1,12 +1,10 @@
@using Modix.Data.Models.Core;
-@using Modix.Services.Core;
@using Modix.Web.Models.Common;
@using Modix.Web.Models.Configuration;
-@using Modix.Web.Models.UserLookup;
@using Modix.Web.Services;
@using MudBlazor
@using Humanizer;
-@using System.Security.Claims;
+@using Modix.Services
Modix - Channels
Channel Designations
@@ -93,7 +91,7 @@
public DiscordHelper DiscordHelper { get; set; } = null!;
[Inject]
- public IDesignatedChannelService DesignatedChannelService { get; set; } = null!;
+ public DesignatedChannelService DesignatedChannelService { get; set; } = null!;
[Inject]
public ISnackbar Snackbar { get; set; } = null!;
@@ -111,7 +109,7 @@
return;
var currentGuild = DiscordHelper.GetUserGuild();
- var designatedChannels = await DesignatedChannelService.GetDesignatedChannelsAsync(currentGuild.Id);
+ var designatedChannels = await DesignatedChannelService.GetDesignatedChannels(currentGuild.Id);
DesignatedChannelMappings = designatedChannels
.Select(d => new DesignatedChannelData(d.Id, d.Channel.Id, d.Type, currentGuild?.GetChannel(d.Channel.Id)?.Name ?? d.Channel.Name))
@@ -143,7 +141,7 @@
var currentGuild = DiscordHelper.GetUserGuild();
var channel = (Discord.IMessageChannel)currentGuild.GetChannel(_selectedChannel!.Id);
- var id = await DesignatedChannelService.AddDesignatedChannelAsync(currentGuild, channel, _selectedDesignatedChannelType!.Value);
+ var id = await DesignatedChannelService.AddDesignatedChannel(currentGuild, channel, _selectedDesignatedChannelType!.Value);
_createDialogVisible = false;
@@ -159,7 +157,7 @@
public async Task RemoveDesignation(long id, DesignatedChannelType designatedChannelType)
{
- await DesignatedChannelService.RemoveDesignatedChannelByIdAsync(id);
+ await DesignatedChannelService.RemoveDesignatedChannelById(id);
var channelMappingsWithType = DesignatedChannelMappings![designatedChannelType];
var removedChannelMapping = channelMappingsWithType.First(x => x.Id == id);
diff --git a/src/Modix/Extensions/ServiceCollectionExtensions.cs b/src/Modix/Extensions/ServiceCollectionExtensions.cs
index 545105f44..76bc0e95a 100644
--- a/src/Modix/Extensions/ServiceCollectionExtensions.cs
+++ b/src/Modix/Extensions/ServiceCollectionExtensions.cs
@@ -161,6 +161,7 @@ public static IServiceCollection AddModix(
services.AddScoped();
services.AddScoped();
services.AddScoped();
+ services.AddScoped();
services.AddMemoryCache();
diff --git a/src/Modix/appsettings.Development.json b/src/Modix/appsettings.Development.json
index 86d7fc55e..9d308bb1a 100644
--- a/src/Modix/appsettings.Development.json
+++ b/src/Modix/appsettings.Development.json
@@ -6,5 +6,6 @@
"Microsoft": "Information"
}
},
- "DbConnection": "Host=127.0.0.1;Username=postgres;Password=password;Database=MODiX-Dev"
-}
+ "DbConnection": "Host=127.0.0.1;Username=postgres;Password=password;Database=MODiX-Dev",
+ "WebsiteBaseUrl": "https://localhost:5000/"
+}
\ No newline at end of file
diff --git a/test/Modix.Data.Test/Repositories/DesignatedChannelMappingRepositoryTests.cs b/test/Modix.Data.Test/Repositories/DesignatedChannelMappingRepositoryTests.cs
deleted file mode 100644
index 27413a043..000000000
--- a/test/Modix.Data.Test/Repositories/DesignatedChannelMappingRepositoryTests.cs
+++ /dev/null
@@ -1,582 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-using Microsoft.EntityFrameworkCore.Infrastructure;
-
-using Modix.Data.Models.Core;
-using Modix.Data.Repositories;
-using Modix.Data.Test.TestData;
-
-using NSubstitute;
-using NUnit.Framework;
-
-using Shouldly;
-
-namespace Modix.Data.Test.Repositories
-{
- public class DesignatedChannelMappingRepositoryTests
- {
- #region Test Context
-
- private static (ModixContext, DesignatedChannelMappingRepository) BuildTestContext()
- {
- var modixContext = TestDataContextFactory.BuildTestDataContext(x =>
- {
- x.Set().AddRange(Users.Entities.Clone());
- x.Set().AddRange(GuildUsers.Entities.Clone());
- x.Set().AddRange(GuildChannels.Entities.Clone());
- x.Set().AddRange(DesignatedChannelMappings.Entities.Clone());
- x.Set().AddRange(ConfigurationActions.Entities.Where(y => !(y.DesignatedChannelMappingId is null)).Clone());
- });
-
- var uut = new DesignatedChannelMappingRepository(modixContext);
-
- return (modixContext, uut);
- }
-
- #endregion Test Context
-
- #region Constructor() Tests
-
- [Test]
- public void Constructor_Always_InvokesBaseConstructor()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- uut.ModixContext.ShouldBeSameAs(modixContext);
- }
-
- #endregion Constructor() Tests
-
- #region BeginCreateTransactionAsync() Tests
-
- [Test]
- [NonParallelizable]
- public async Task BeginCreateTransactionAsync_CreateTransactionIsInProgress_WaitsForCompletion()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var existingTransaction = await uut.BeginCreateTransactionAsync();
-
- var result = uut.BeginCreateTransactionAsync();
-
- result.IsCompleted.ShouldBeFalse();
-
- existingTransaction.Dispose();
- (await result).Dispose();
- }
-
- [Test]
- [NonParallelizable]
- public async Task BeginCreateTransactionAsync_CreateTransactionIsNotInProgress_ReturnsImmediately()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = uut.BeginCreateTransactionAsync();
-
- result.IsCompleted.ShouldBeTrue();
-
- (await result).Dispose();
- }
-
- [Test]
- [NonParallelizable]
- public async Task BeginCreateTransactionAsync_DeleteTransactionIsInProgress_ReturnsImmediately()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var deleteTransaction = await uut.BeginDeleteTransactionAsync();
-
- var result = uut.BeginCreateTransactionAsync();
-
- result.IsCompleted.ShouldBeTrue();
-
- deleteTransaction.Dispose();
- (await result).Dispose();
- }
-
- [Test]
- [NonParallelizable]
- public async Task BeginCreateTransactionAsync_Always_TransactionIsForContextDatabase()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var database = Substitute.ForPartsOf(modixContext);
- modixContext.Database.Returns(database);
-
- using (var transaction = await uut.BeginCreateTransactionAsync()) { }
-
- await database.ShouldHaveReceived(1)
- .BeginTransactionAsync();
- }
-
- #endregion BeginCreateTransactionAsync() Tests
-
- #region BeginDeleteTransactionAsync() Tests
-
- [Test]
- [NonParallelizable]
- public async Task BeginDeleteTransactionAsync_DeleteTransactionIsInProgress_WaitsForCompletion()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var existingTransaction = await uut.BeginDeleteTransactionAsync();
-
- var result = uut.BeginDeleteTransactionAsync();
-
- result.IsCompleted.ShouldBeFalse();
-
- existingTransaction.Dispose();
- (await result).Dispose();
- }
-
- [Test]
- [NonParallelizable]
- public async Task BeginDeleteTransactionAsync_DeleteTransactionIsNotInProgress_ReturnsImmediately()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = uut.BeginDeleteTransactionAsync();
-
- result.IsCompleted.ShouldBeTrue();
-
- (await result).Dispose();
- }
-
- [Test]
- [NonParallelizable]
- public async Task BeginDeleteTransactionAsync_CreateTransactionIsInProgress_ReturnsImmediately()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var createTransaction = await uut.BeginCreateTransactionAsync();
-
- var result = uut.BeginDeleteTransactionAsync();
-
- result.IsCompleted.ShouldBeTrue();
-
- createTransaction.Dispose();
- (await result).Dispose();
- }
-
- [Test]
- [NonParallelizable]
- public async Task BeginDeleteTransactionAsync_Always_TransactionIsForContextDatabase()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var database = Substitute.ForPartsOf(modixContext);
- modixContext.Database.Returns(database);
-
- using (var transaction = await uut.BeginDeleteTransactionAsync()) { }
-
- await database.ShouldHaveReceived(1)
- .BeginTransactionAsync();
- }
-
- #endregion BeginDeleteTransactionAsync() Tests
-
- #region CreateAsync() Tests
-
- [Test]
- public async Task CreateAsync_DataIsNull_DoesNotUpdateDesignatedChannelMappingsAndThrowsException()
- {
- (var modixContext, var uut) = BuildTestContext();
-
- await Should.ThrowAsync(uut.CreateAsync(null!));
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(DesignatedChannelMappings.Entities.Select(x => x.Id));
-
- modixContext.Set().EachShould(x => x.ShouldNotHaveChanged());
-
- await modixContext.ShouldNotHaveReceived()
- .SaveChangesAsync();
- }
-
- [TestCaseSource(nameof(DesignatedChannelMappingCreationTestCases))]
- public async Task CreateAsync_DataIsNotNull_InsertsDesignatedChannelMapping(DesignatedChannelMappingCreationData data)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var id = await uut.CreateAsync(data);
-
- modixContext.Set().ShouldContain(x => x.Id == id);
- var designatedChannelMapping = modixContext.Set().First(x => x.Id == id);
-
- designatedChannelMapping.GuildId.ShouldBe(data.GuildId);
- designatedChannelMapping.Type.ShouldBe(data.Type);
- designatedChannelMapping.ChannelId.ShouldBe(data.ChannelId);
- designatedChannelMapping.DeleteActionId.ShouldBeNull();
-
- modixContext.Set().Where(x => x.Id != designatedChannelMapping.Id).Select(x => x.Id).ShouldBe(DesignatedChannelMappings.Entities.Select(x => x.Id));
- modixContext.Set().Where(x => x.Id != designatedChannelMapping.Id).EachShould(x => x.ShouldNotHaveChanged());
-
- modixContext.Set().ShouldContain(x => x.Id == designatedChannelMapping.CreateActionId);
- var createAction = modixContext.Set().First(x => x.Id == designatedChannelMapping.CreateActionId);
-
- createAction.GuildId.ShouldBe(data.GuildId);
- createAction.Type.ShouldBe(ConfigurationActionType.DesignatedChannelMappingCreated);
- createAction.Created.ShouldBeInRange(
- DateTimeOffset.UtcNow - TimeSpan.FromSeconds(1),
- DateTimeOffset.UtcNow + TimeSpan.FromSeconds(1));
- createAction.CreatedById.ShouldBe(data.CreatedById);
- createAction.ClaimMappingId.ShouldBeNull();
- createAction.DesignatedChannelMappingId.ShouldBe(designatedChannelMapping.Id);
- createAction.DesignatedRoleMappingId.ShouldBeNull();
-
- modixContext.Set().Where(x => x.Id != createAction.Id).Select(x => x.Id).ShouldBe(ConfigurationActions.Entities.Where(x => !(x.DesignatedChannelMappingId is null)).Select(x => x.Id));
- modixContext.Set().Where(x => x.Id != createAction.Id).EachShould(x => x.ShouldNotHaveChanged());
-
- await modixContext.ShouldHaveReceived(2)
- .SaveChangesAsync();
- }
-
- #endregion CreateAsync() Tests
-
- #region AnyAsync() Tests
-
- [TestCaseSource(nameof(ValidSearchCriteriaTestCases))]
- public async Task AnyAsync_DesignatedChannelMappingsExist_ReturnsTrue(DesignatedChannelMappingSearchCriteria criteria)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.AnyAsync(criteria, default);
-
- result.ShouldBeTrue();
- }
-
- [TestCaseSource(nameof(InvalidSearchCriteriaTestCases))]
- public async Task AnyAsync_DesignatedChannelMappingsDoNotExist_ReturnsFalse(DesignatedChannelMappingSearchCriteria criteria)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.AnyAsync(criteria, default);
-
- result.ShouldBeFalse();
- }
-
- #endregion AnyAsync() Tests
-
- #region SearchChannelIdsAsync() Tests
-
- [TestCaseSource(nameof(ValidSearchCriteriaAndResultIdsTestCases))]
- public async Task SearchChannelIdsAsync_DesignatedChannelMappingsExist_ReturnsMatchingIds(DesignatedChannelMappingSearchCriteria criteria, long[] resultIds)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.SearchChannelIdsAsync(criteria);
-
- result.ShouldNotBeNull();
- result.ShouldBe(resultIds.Select(x => DesignatedChannelMappings.Entities.First(y => y.Id == x).ChannelId));
- }
-
- [TestCaseSource(nameof(InvalidSearchCriteriaTestCases))]
- public async Task SearchChannelIdsAsync_DesignatedChannelMappingsDoNotExist_ReturnsEmpty(DesignatedChannelMappingSearchCriteria criteria)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.SearchChannelIdsAsync(criteria);
-
- result.ShouldNotBeNull();
- result.ShouldBeEmpty();
- }
-
- #endregion SearchChannelIdsAsync() Tests
-
- #region SearchBriefsAsync() Tests
-
- [TestCaseSource(nameof(ValidSearchCriteriaAndResultIdsTestCases))]
- public async Task SearchBriefsAsync_DesignatedChannelMappingsExist_ReturnsMatchingBriefs(DesignatedChannelMappingSearchCriteria criteria, long[] resultIds)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.SearchBriefsAsync(criteria);
-
- result.ShouldNotBeNull();
- result.Select(x => x.Id).ShouldBe(resultIds);
- result.EachShould(x => x.ShouldMatchTestData());
- }
-
- [TestCaseSource(nameof(InvalidSearchCriteriaTestCases))]
- public async Task SearchBriefsAsync_DesignatedChannelMappingsDoNotExist_ReturnsEmpty(DesignatedChannelMappingSearchCriteria criteria)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.SearchBriefsAsync(criteria);
-
- result.ShouldNotBeNull();
- result.ShouldBeEmpty();
- }
-
- #endregion SearchBriefsAsync() Tests
-
- #region DeleteAsync() Tests
-
- [TestCaseSource(nameof(ValidSearchCriteriaAndValidUserIdAndResultIdsTestCases))]
- public async Task DeleteAsync_DesignatedChannelMappingsAreNotDeleted_UpdatesDesignatedChannelMappingsAndReturnsCount(DesignatedChannelMappingSearchCriteria criteria, ulong deletedById, long[] designatedChannelMappingIds)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.DeleteAsync(criteria, deletedById);
-
- result.ShouldBe(designatedChannelMappingIds
- .Where(x => DesignatedChannelMappings.Entities
- .Any(y => (y.Id == x) && (y.DeleteActionId == null)))
- .Count());
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(DesignatedChannelMappings.Entities.Select(x => x.Id));
-
- modixContext.Set()
- .Where(x => designatedChannelMappingIds.Contains(x.Id) && (x.DeleteActionId == null))
- .EachShould(entity =>
- {
- var originalEntity = DesignatedChannelMappings.Entities.First(x => x.Id == entity.Id);
-
- entity.GuildId.ShouldBe(originalEntity.GuildId);
- entity.ChannelId.ShouldBe(originalEntity.ChannelId);
- entity.Type.ShouldBe(originalEntity.Type);
- entity.CreateActionId.ShouldBe(originalEntity.CreateActionId);
- entity.DeleteActionId.ShouldNotBeNull();
-
- modixContext.Set().ShouldContain(x => x.Id == entity.DeleteActionId);
- var deleteAction = modixContext.Set().First(x => x.Id == entity.DeleteActionId);
-
- deleteAction.GuildId.ShouldBe(entity.GuildId);
- deleteAction.Type.ShouldBe(ConfigurationActionType.DesignatedChannelMappingDeleted);
- deleteAction.Created.ShouldBeInRange(
- DateTimeOffset.UtcNow - TimeSpan.FromMinutes(1),
- DateTimeOffset.UtcNow + TimeSpan.FromMinutes(1));
- deleteAction.CreatedById.ShouldBe(deletedById);
- deleteAction.ClaimMappingId.ShouldBeNull();
- deleteAction.DesignatedChannelMappingId.ShouldBe(entity.Id);
- deleteAction.DesignatedRoleMappingId.ShouldBeNull();
- });
-
- modixContext.Set()
- .AsEnumerable()
- .Where(x => !designatedChannelMappingIds.Contains(x.Id) || DesignatedChannelMappings.Entities
- .Any(y => (y.Id == x.Id) && (x.DeleteActionId == null)))
- .EachShould(x => x.ShouldNotHaveChanged());
-
- modixContext.Set()
- .AsEnumerable()
- .Where(x => DesignatedChannelMappings.Entities
- .Any(y => (y.DeleteActionId == x.Id) && designatedChannelMappingIds.Contains(y.Id)))
- .EachShould(x => x.ShouldNotHaveChanged());
-
- await modixContext.ShouldHaveReceived(1)
- .SaveChangesAsync();
- }
-
- [TestCaseSource(nameof(InvalidSearchCriteriaAndValidUserIdTestCases))]
- public async Task DeleteAsync_DesignatedChannelMappingsDoNotExist_DoesNotUpdateDesignatedChannelMappingsAndReturns0(DesignatedChannelMappingSearchCriteria criteria, ulong deletedById)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.DeleteAsync(criteria, deletedById);
-
- result.ShouldBe(0);
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(DesignatedChannelMappings.Entities
- .Select(x => x.Id));
-
- modixContext.Set()
- .EachShould(x => x.ShouldNotHaveChanged());
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(ConfigurationActions.Entities
- .Where(x => x.DesignatedChannelMappingId != null)
- .Select(x => x.Id));
-
- modixContext.Set()
- .EachShould(x => x.ShouldNotHaveChanged());
-
- await modixContext.ShouldHaveReceived(1)
- .SaveChangesAsync();
- }
-
- #endregion DeleteAsync() Tests
-
- #region TryDeleteAsync() Tests
-
- [TestCaseSource(nameof(ActiveDesignatedChannelMappingAndValidUserIdTestCases))]
- public async Task TryDeleteAsync_DesignatedChannelMappingIsNotDeleted_UpdatesDesignatedChannelMappingAndReturnsTrue(long designatedChannelMappingId, ulong deletedById)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.TryDeleteAsync(designatedChannelMappingId, deletedById);
-
- result.ShouldBeTrue();
-
- modixContext.Set()
- .ShouldContain(x => x.Id == designatedChannelMappingId);
- var designatedChannelMapping = modixContext.Set()
- .First(x => x.Id == designatedChannelMappingId);
-
- var originalDesignatedChannelMapping = DesignatedChannelMappings.Entities
- .First(x => x.Id == designatedChannelMappingId);
-
- designatedChannelMapping.GuildId.ShouldBe(originalDesignatedChannelMapping.GuildId);
- designatedChannelMapping.ChannelId.ShouldBe(originalDesignatedChannelMapping.ChannelId);
- designatedChannelMapping.Type.ShouldBe(originalDesignatedChannelMapping.Type);
- designatedChannelMapping.CreateActionId.ShouldBe(originalDesignatedChannelMapping.CreateActionId);
- designatedChannelMapping.DeleteActionId.ShouldNotBeNull();
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(DesignatedChannelMappings.Entities
- .Select(x => x.Id));
-
- modixContext.Set()
- .Where(x => x.Id != designatedChannelMappingId)
- .EachShould(x => x.ShouldNotHaveChanged());
-
- modixContext.Set()
- .ShouldContain(x => x.Id == designatedChannelMapping.DeleteActionId);
- var deleteAction = modixContext.Set()
- .First(x => x.Id == designatedChannelMapping.DeleteActionId);
-
- deleteAction.GuildId.ShouldBe(designatedChannelMapping.GuildId);
- deleteAction.Type.ShouldBe(ConfigurationActionType.DesignatedChannelMappingDeleted);
- deleteAction.Created.ShouldBeInRange(
- DateTimeOffset.UtcNow - TimeSpan.FromSeconds(1),
- DateTimeOffset.UtcNow + TimeSpan.FromSeconds(1));
- deleteAction.CreatedById.ShouldBe(deletedById);
- deleteAction.ClaimMappingId.ShouldBeNull();
- deleteAction.DesignatedChannelMappingId.ShouldBe(designatedChannelMapping.Id);
- deleteAction.DesignatedRoleMappingId.ShouldBeNull();
-
- modixContext.Set()
- .Where(x => x.Id != deleteAction.Id)
- .Select(x => x.Id)
- .ShouldBe(ConfigurationActions.Entities
- .Where(x => !(x.DesignatedChannelMappingId is null))
- .Select(x => x.Id));
-
- modixContext.Set()
- .Where(x => x.Id != deleteAction.Id)
- .EachShould(x => x.ShouldNotHaveChanged());
-
- await modixContext.ShouldHaveReceived(1)
- .SaveChangesAsync();
- }
-
- [TestCaseSource(nameof(DeletedDesignatedChannelMappingAndValidUserIdTestCases))]
- [TestCaseSource(nameof(InvalidDesignatedChannelMappingAndValidUserIdTestCases))]
- public async Task TryDeleteAsync_DesignatedChannelMappingIsDeleted_DoesNotUpdateDesignatedChannelMappingsAndReturnsFalse(long designatedChannelMappingId, ulong deletedById)
- {
- (var modixContext, var uut) = BuildTestContext();
-
- var result = await uut.TryDeleteAsync(designatedChannelMappingId, deletedById);
-
- result.ShouldBeFalse();
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(DesignatedChannelMappings.Entities
- .Select(x => x.Id));
-
- modixContext.Set()
- .EachShould(x => x.ShouldNotHaveChanged());
-
- modixContext.Set()
- .AsQueryable()
- .Select(x => x.Id)
- .ShouldBe(ConfigurationActions.Entities
- .Where(x => !(x.DesignatedChannelMappingId is null))
- .Select(x => x.Id));
-
- modixContext.Set()
- .EachShould(x => x.ShouldNotHaveChanged());
-
- await modixContext.ShouldNotHaveReceived()
- .SaveChangesAsync();
- }
-
- #endregion TryDeleteAsync() Tests
-
- #region Test Data
-
- public static readonly IEnumerable DesignatedChannelMappingCreationTestCases
- = DesignatedChannelMappings.Creations
- .Select(x => new TestCaseData(x)
- .SetName($"{{m}}({x.GuildId}, {x.ChannelId}, {x.Type})"));
-
- public static readonly IEnumerable ValidSearchCriteriaTestCases
- = DesignatedChannelMappings.Searches
- .Where(x => x.resultIds.Any())
- .Select(x => new TestCaseData(x.criteria)
- .SetName($"{{m}}({x.name})"));
-
- public static readonly IEnumerable ValidSearchCriteriaAndResultIdsTestCases
- = DesignatedChannelMappings.Searches
- .Where(x => x.resultIds.Any())
- .Select(x => new TestCaseData(x.criteria, x.resultIds)
- .SetName($"{{m}}({x.name})"));
-
- public static readonly IEnumerable ValidSearchCriteriaAndValidUserIdAndResultIdsTestCases
- = DesignatedChannelMappings.Searches
- .Where(x => x.resultIds.Any())
- .SelectMany(x => Users.Entities
- .Where(y => DesignatedChannelMappings.Entities
- .Where(z => x.resultIds.Contains(z.Id))
- .Select(z => z.GuildId)
- .Distinct()
- .All(z => GuildUsers.Entities
- .Any(w => (w.UserId == y.Id) && (w.GuildId == z))))
- .Select(y => new TestCaseData(x.criteria, y.Id, x.resultIds)
- .SetName($"{{m}}(\"{x.name}\", {y.Id})")));
-
- public static readonly IEnumerable InvalidSearchCriteriaTestCases
- = DesignatedChannelMappings.Searches
- .Where(x => !x.resultIds.Any())
- .Select(x => new TestCaseData(x.criteria)
- .SetName($"{{m}}({x.name})"));
-
- public static readonly IEnumerable InvalidSearchCriteriaAndValidUserIdTestCases
- = DesignatedChannelMappings.Searches
- .Where(x => !x.resultIds.Any())
- .SelectMany(x => Users.Entities
- .Select(y => new TestCaseData(x.criteria, y.Id)
- .SetName($"{{m}}(\"{x.name}\", {y.Id})")));
-
- public static readonly IEnumerable ActiveDesignatedChannelMappingAndValidUserIdTestCases
- = DesignatedChannelMappings.Entities
- .Where(x => x.DeleteActionId is null)
- .SelectMany(x => GuildUsers.Entities
- .Where(y => y.GuildId == x.GuildId)
- .Select(y => new TestCaseData(x.Id, y.UserId)));
-
- public static readonly IEnumerable DeletedDesignatedChannelMappingAndValidUserIdTestCases
- = DesignatedChannelMappings.Entities
- .Where(x => !(x.DeleteActionId is null))
- .SelectMany(x => GuildUsers.Entities
- .Where(y => y.GuildId == x.GuildId)
- .Select(y => new TestCaseData(x.Id, y.UserId)));
-
- public static readonly IEnumerable InvalidDesignatedChannelMappingAndValidUserIdTestCases
- = Enumerable.Empty()
- .Append(DesignatedChannelMappings.Entities
- .Select(x => x.Id)
- .Max() + 1)
- .SelectMany(x => Users.Entities
- .Select(y => new TestCaseData(x, y.Id)));
-
- #endregion Test Data
- }
-}