Skip to content

Commit fcb4b8c

Browse files
Merge pull request #1039 from discord-csharp/start-moderation-refactor
V3: Start moderation refactor, add scoped session concept
2 parents 063417e + 55a6b3e commit fcb4b8c

34 files changed

+973
-1021
lines changed

src/Modix.Bot/Behaviors/ModerationLoggingBehavior.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public ModerationLoggingBehavior(
3838
DesignatedChannelService = designatedChannelService;
3939
Config = config.Value;
4040

41-
_lazyModerationService = new Lazy<IModerationService>(() => serviceProvider.GetRequiredService<IModerationService>());
41+
_lazyModerationService = new Lazy<ModerationService>(() => serviceProvider.GetRequiredService<ModerationService>());
4242
}
4343

4444
/// <inheritdoc />
@@ -95,9 +95,9 @@ public async Task OnModerationActionCreatedAsync(long moderationActionId, Modera
9595
/// <summary>
9696
/// An <see cref="IModerationService"/> for performing moderation actions.
9797
/// </summary>
98-
internal protected IModerationService ModerationService
98+
internal protected ModerationService ModerationService
9999
=> _lazyModerationService.Value;
100-
private readonly Lazy<IModerationService> _lazyModerationService;
100+
private readonly Lazy<ModerationService> _lazyModerationService;
101101

102102
internal protected static ModixConfig Config { get; private set; }
103103

src/Modix.Bot/DiscordBotSession.cs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Discord.Commands;
5+
using Discord.WebSocket;
6+
using Modix.Data.Models.Core;
7+
using Modix.Services;
8+
9+
namespace Modix.Bot;
10+
11+
public class DiscordBotSession(DiscordSocketClient discordSocketClient,
12+
AuthorizationClaimService authorizationClaimService) : IScopedSession
13+
{
14+
public ulong SelfUserId { get; } = discordSocketClient.CurrentUser.Id;
15+
16+
private ulong _executingUserId;
17+
18+
public ulong ExecutingUserId =>
19+
_executingUserId == default
20+
? SelfUserId
21+
: _executingUserId;
22+
23+
private IReadOnlyCollection<AuthorizationClaim> _authorizationClaims;
24+
25+
public void ApplyCommandContext(ICommandContext context)
26+
{
27+
_executingUserId = context.User.Id;
28+
}
29+
30+
private async Task<IReadOnlyCollection<AuthorizationClaim>> GetClaims()
31+
{
32+
return _authorizationClaims ??= await authorizationClaimService.GetClaimsForUser(ExecutingUserId);
33+
}
34+
35+
public async Task<bool> HasClaim(params AuthorizationClaim[] claims)
36+
{
37+
var ownedClaims = await GetClaims();
38+
return claims.All(claim => ownedClaims.Contains(claim));
39+
}
40+
}

src/Modix.Bot/ModixBot.cs

+63-34
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Microsoft.Extensions.Options;
1818
using Modix.Bot.Notifications;
1919
using Modix.Data.Models.Core;
20+
using Modix.Services;
2021

2122
namespace Modix.Bot
2223
{
@@ -61,6 +62,12 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
6162
discordSocketClient.MessageDeleted += OnMessageDeleted;
6263
discordSocketClient.ReactionAdded += OnReactionAdded;
6364
discordSocketClient.ReactionRemoved += OnReactionRemoved;
65+
discordSocketClient.UserJoined += OnUserJoined;
66+
discordSocketClient.AuditLogCreated += OnAuditLogCreated;
67+
discordSocketClient.GuildAvailable += OnGuildAvailable;
68+
discordSocketClient.ChannelCreated += OnChannelCreated;
69+
discordSocketClient.ChannelUpdated += OnChannelUpdated;
70+
discordSocketClient.JoinedGuild += OnJoinedGuild;
6471

6572
discordRestClient.Log += discordSerilogAdapter.HandleLog;
6673
commandService.Log += discordSerilogAdapter.HandleLog;
@@ -77,7 +84,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
7784
await commandService.AddModulesAsync(typeof(ModixBot).Assembly, _scope.ServiceProvider);
7885

7986
logger.LogInformation("{Modules} modules loaded, containing {Commands} commands",
80-
commandService.Modules.Count(), commandService.Modules.SelectMany(d=>d.Commands).Count());
87+
commandService.Modules.Count(), commandService.Modules.SelectMany(d => d.Commands).Count());
8188

8289
logger.LogInformation("Logging into Discord and starting the client");
8390

@@ -87,18 +94,24 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
8794

8895
logger.LogInformation("Loading interaction modules...");
8996

90-
var modules = (await interactionService.AddModulesAsync(typeof(ModixBot).Assembly, _scope.ServiceProvider)).ToArray();
97+
var modules =
98+
(await interactionService.AddModulesAsync(typeof(ModixBot).Assembly, _scope.ServiceProvider))
99+
.ToArray();
91100

92101
foreach (var guild in discordSocketClient.Guilds)
93102
{
94103
var commands = await interactionService.AddModulesToGuildAsync(guild, deleteMissing: true, modules);
95104
}
96105

97106
logger.LogInformation("{Modules} interaction modules loaded", modules.Length);
98-
logger.LogInformation("Loaded {SlashCommands} slash commands", modules.SelectMany(x => x.SlashCommands).Count());
99-
logger.LogInformation("Loaded {ContextCommands} context commands", modules.SelectMany(x => x.ContextCommands).Count());
100-
logger.LogInformation("Loaded {ModalCommands} modal commands", modules.SelectMany(x => x.ModalCommands).Count());
101-
logger.LogInformation("Loaded {ComponentCommands} component commands", modules.SelectMany(x => x.ComponentCommands).Count());
107+
logger.LogInformation("Loaded {SlashCommands} slash commands",
108+
modules.SelectMany(x => x.SlashCommands).Count());
109+
logger.LogInformation("Loaded {ContextCommands} context commands",
110+
modules.SelectMany(x => x.ContextCommands).Count());
111+
logger.LogInformation("Loaded {ModalCommands} modal commands",
112+
modules.SelectMany(x => x.ModalCommands).Count());
113+
logger.LogInformation("Loaded {ComponentCommands} component commands",
114+
modules.SelectMany(x => x.ComponentCommands).Count());
102115

103116
await Task.Delay(-1, stoppingToken);
104117
}
@@ -154,7 +167,7 @@ private Task OnDisconnect(Exception ex)
154167
{
155168
// Reconnections are handled by Discord.NET, we
156169
// don't need to worry about handling this ourselves
157-
if(ex is GatewayReconnectException)
170+
if (ex is GatewayReconnectException)
158171
{
159172
logger.LogInformation("Received gateway reconnect");
160173
return Task.CompletedTask;
@@ -171,7 +184,6 @@ private async Task StartClient(CancellationToken cancellationToken)
171184

172185
try
173186
{
174-
175187
cancellationToken.ThrowIfCancellationRequested();
176188

177189
await discordSocketClient.LoginAsync(TokenType.Bot, modixConfig.Value.DiscordToken);
@@ -193,14 +205,18 @@ private void UnregisterClientHandlers()
193205
discordSocketClient.LatencyUpdated -= OnLatencyUpdated;
194206
discordSocketClient.Disconnected -= OnDisconnect;
195207
discordSocketClient.Log -= discordSerilogAdapter.HandleLog;
196-
197208
discordSocketClient.Ready -= OnClientReady;
198-
199209
discordSocketClient.MessageReceived -= OnMessageReceived;
200210
discordSocketClient.MessageUpdated -= OnMessageUpdated;
201211
discordSocketClient.MessageDeleted -= OnMessageDeleted;
202212
discordSocketClient.ReactionAdded -= OnReactionAdded;
203213
discordSocketClient.ReactionRemoved -= OnReactionRemoved;
214+
discordSocketClient.UserJoined -= OnUserJoined;
215+
discordSocketClient.AuditLogCreated -= OnAuditLogCreated;
216+
discordSocketClient.GuildAvailable -= OnGuildAvailable;
217+
discordSocketClient.ChannelCreated -= OnChannelCreated;
218+
discordSocketClient.ChannelUpdated -= OnChannelUpdated;
219+
discordSocketClient.JoinedGuild -= OnJoinedGuild;
204220
}
205221

206222
private async Task OnClientReady()
@@ -209,40 +225,53 @@ private async Task OnClientReady()
209225
_whenReadySource.SetResult(null);
210226
}
211227

212-
private async Task OnMessageReceived(SocketMessage arg)
228+
private async Task PublishMessage<T>(T message) where T : INotification
213229
{
214230
using var scope = serviceProvider.CreateScope();
215-
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
216-
await mediator.Publish(new MessageReceivedNotificationV3(arg));
231+
await PublishMessage(scope, message);
217232
}
218233

219-
private async Task OnMessageUpdated(Cacheable<IMessage, ulong> cachedMessage, SocketMessage newMessage, ISocketMessageChannel channel)
234+
private async Task PublishMessage<T>(IServiceScope scope, T message) where T : INotification
220235
{
221-
using var scope = serviceProvider.CreateScope();
222236
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
223-
await mediator.Publish(new MessageUpdatedNotificationV3(cachedMessage, newMessage, channel));
237+
await mediator.Publish(message);
224238
}
225239

226-
private async Task OnMessageDeleted(Cacheable<IMessage, ulong> message, Cacheable<IMessageChannel, ulong> channel)
227-
{
228-
using var scope = serviceProvider.CreateScope();
229-
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
230-
await mediator.Publish(new MessageDeletedNotificationV3(message, channel));
231-
}
240+
private Task OnMessageReceived(SocketMessage message) =>
241+
PublishMessage(new MessageReceivedNotificationV3(message));
232242

233-
private async Task OnReactionAdded(Cacheable<IUserMessage, ulong> message, Cacheable<IMessageChannel, ulong> channel, SocketReaction reaction)
234-
{
235-
using var scope = serviceProvider.CreateScope();
236-
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
237-
await mediator.Publish(new ReactionAddedNotificationV3(message, channel, reaction));
238-
}
243+
private Task OnMessageUpdated(Cacheable<IMessage, ulong> cachedMessage, SocketMessage newMessage,
244+
ISocketMessageChannel channel) =>
245+
PublishMessage(new MessageUpdatedNotificationV3(cachedMessage, newMessage, channel));
239246

240-
private async Task OnReactionRemoved(Cacheable<IUserMessage, ulong> message, Cacheable<IMessageChannel, ulong> channel, SocketReaction reaction)
241-
{
242-
using var scope = serviceProvider.CreateScope();
243-
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
244-
await mediator.Publish(new ReactionRemovedNotificationV3(message, channel, reaction));
245-
}
247+
private Task OnMessageDeleted(Cacheable<IMessage, ulong> message,
248+
Cacheable<IMessageChannel, ulong> channel) =>
249+
PublishMessage(new MessageDeletedNotificationV3(message, channel));
250+
251+
private Task OnReactionAdded(Cacheable<IUserMessage, ulong> message,
252+
Cacheable<IMessageChannel, ulong> channel, SocketReaction reaction) =>
253+
PublishMessage(new ReactionAddedNotificationV3(message, channel, reaction));
254+
255+
private Task OnReactionRemoved(Cacheable<IUserMessage, ulong> message,
256+
Cacheable<IMessageChannel, ulong> channel, SocketReaction reaction) =>
257+
PublishMessage(new ReactionRemovedNotificationV3(message, channel, reaction));
258+
259+
private Task OnUserJoined(SocketGuildUser guildUser) =>
260+
PublishMessage(new UserJoinedNotificationV3(guildUser));
261+
262+
private Task OnAuditLogCreated(SocketAuditLogEntry entry, SocketGuild guild) =>
263+
PublishMessage(new AuditLogCreatedNotificationV3(entry, guild));
264+
265+
private Task OnGuildAvailable(SocketGuild guild)
266+
=> PublishMessage(new GuildAvailableNotificationV3(guild));
267+
268+
private Task OnChannelCreated(SocketChannel channel) =>
269+
PublishMessage(new ChannelCreatedNotificationV3(channel));
270+
271+
private Task OnChannelUpdated(SocketChannel oldChannel, SocketChannel newChannel) =>
272+
PublishMessage(new ChannelUpdatedNotificationV3(oldChannel, newChannel));
273+
274+
private Task OnJoinedGuild(SocketGuild guild) => PublishMessage(new JoinedGuildNotificationV3(guild));
246275

247276
public override void Dispose()
248277
{

src/Modix.Bot/Modules/InfractionModule.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ namespace Modix.Modules
2424
[ModuleHelp("Infractions", "Provides commands for working with infractions.")]
2525
public class InfractionModule : InteractionModuleBase
2626
{
27-
private readonly IModerationService _moderationService;
27+
private readonly ModerationService _moderationService;
2828
private readonly ModixConfig _config;
2929

30-
public InfractionModule(IModerationService moderationService, IOptions<ModixConfig> config)
30+
public InfractionModule(ModerationService moderationService, IOptions<ModixConfig> config)
3131
{
3232
_moderationService = moderationService;
3333
_config = config.Value;

src/Modix.Bot/Modules/ModerationModule.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace Modix.Modules
2626
public class ModerationModule : ModuleBase
2727
{
2828
public ModerationModule(
29-
IModerationService moderationService,
29+
ModerationService moderationService,
3030
IUserService userService,
3131
IOptions<ModixConfig> config)
3232
{
@@ -306,7 +306,7 @@ private async ValueTask<bool> GetConfirmationIfRequiredAsync(DiscordUserOrMessag
306306
+ $"{Format.Bold(author.GetDisplayName())} ({userOrAuthor.UserId}), the message's author?");
307307
}
308308

309-
internal protected IModerationService ModerationService { get; }
309+
internal protected ModerationService ModerationService { get; }
310310

311311
internal protected IUserService UserService { get; }
312312

src/Modix.Bot/Modules/UserInfoModule.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public class UserInfoModule : InteractionModuleBase
3737
{
3838
private readonly ILogger<UserInfoModule> _log;
3939
private readonly IUserService _userService;
40-
private readonly IModerationService _moderationService;
40+
private readonly ModerationService _moderationService;
4141
private readonly IAuthorizationService _authorizationService;
4242
private readonly IMessageRepository _messageRepository;
4343
private readonly IEmojiRepository _emojiRepository;
@@ -51,7 +51,7 @@ public class UserInfoModule : InteractionModuleBase
5151
public UserInfoModule(
5252
ILogger<UserInfoModule> logger,
5353
IUserService userService,
54-
IModerationService moderationService,
54+
ModerationService moderationService,
5555
IAuthorizationService authorizationService,
5656
IMessageRepository messageRepository,
5757
IEmojiRepository emojiRepository,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Discord.WebSocket;
2+
using MediatR;
3+
4+
namespace Modix.Bot.Notifications;
5+
6+
public class AuditLogCreatedNotificationV3(SocketAuditLogEntry entry, SocketGuild guild) : INotification
7+
{
8+
public SocketAuditLogEntry Entry { get; } = entry;
9+
public SocketGuild Guild { get; } = guild;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Discord.WebSocket;
2+
using MediatR;
3+
4+
namespace Modix.Bot.Notifications;
5+
6+
public class ChannelCreatedNotificationV3(SocketChannel channel) : INotification
7+
{
8+
public SocketChannel Channel { get; } = channel;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using Discord.WebSocket;
2+
using MediatR;
3+
4+
namespace Modix.Bot.Notifications;
5+
6+
public class ChannelUpdatedNotificationV3(SocketChannel oldChannel, SocketChannel newChannel) : INotification
7+
{
8+
public SocketChannel OldChannel { get; } = oldChannel;
9+
public SocketChannel NewChannel { get; } = newChannel;
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Discord.WebSocket;
2+
using MediatR;
3+
4+
namespace Modix.Bot.Notifications;
5+
6+
public class GuildAvailableNotificationV3(SocketGuild guild) : INotification
7+
{
8+
public SocketGuild Guild { get; } = guild;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Discord.WebSocket;
2+
using MediatR;
3+
4+
namespace Modix.Bot.Notifications;
5+
6+
public class JoinedGuildNotificationV3(SocketGuild guild) : INotification
7+
{
8+
public SocketGuild Guild { get; } = guild;
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using Discord.WebSocket;
2+
using MediatR;
3+
4+
namespace Modix.Bot.Notifications;
5+
6+
public class UserJoinedNotificationV3(SocketGuildUser guildUser) : INotification
7+
{
8+
public SocketGuildUser GuildUser { get; } = guildUser;
9+
}

0 commit comments

Comments
 (0)