diff --git a/src/Kattbot/Infrastructure/NotificationPublisher.cs b/src/Kattbot/Infrastructure/NotificationPublisher.cs
index 3f9e140..b98ac82 100644
--- a/src/Kattbot/Infrastructure/NotificationPublisher.cs
+++ b/src/Kattbot/Infrastructure/NotificationPublisher.cs
@@ -24,7 +24,7 @@ public Task Publish(INotification notification, CancellationToken cancellationTo
///
/// Sauce: https://github.com/jbogard/MediatR/blob/master/samples/MediatR.Examples.PublishStrategies/Publisher.cs.
///
- private async Task SyncContinueOnException(
+ private static async Task SyncContinueOnException(
IEnumerable handlers,
INotification notification,
CancellationToken cancellationToken)
@@ -41,7 +41,7 @@ private async Task SyncContinueOnException(
{
exceptions.AddRange(ex.Flatten().InnerExceptions);
}
- catch (Exception ex) when (!(ex is OutOfMemoryException || ex is StackOverflowException))
+ catch (Exception ex) when (ex is not (OutOfMemoryException or StackOverflowException))
{
exceptions.Add(ex);
}
diff --git a/src/Kattbot/Infrastructure/ServiceCollectionExtensions.cs b/src/Kattbot/Infrastructure/ServiceCollectionExtensions.cs
index 0e3133c..d6be40f 100644
--- a/src/Kattbot/Infrastructure/ServiceCollectionExtensions.cs
+++ b/src/Kattbot/Infrastructure/ServiceCollectionExtensions.cs
@@ -36,34 +36,29 @@ public static void AddDiscordClient(this IServiceCollection services, IConfigura
var clientBuilder = DiscordClientBuilder.CreateDefault(botToken, DiscordIntents.All, services);
+ clientBuilder.ConfigureExtraFeatures(cfg => { cfg.LogUnknownEvents = false; });
+
clientBuilder.SetLogLevel(logLevel);
clientBuilder.RegisterCommands(configuration);
clientBuilder.RegisterEventHandlers();
- // This replacement has to happen after the DiscordClientBuilder.CreateDefault call
- // and before the DiscordClient is built.
- services.Replace();
-
// Calling build registers the DiscordClient as a singleton in the service collection
clientBuilder.Build();
}
public static void AddDbContext(this IServiceCollection services, IConfiguration configuration)
{
- services.AddDbContext(
- builder =>
- {
- var dbConnString = configuration.GetValue("Kattbot:ConnectionString");
- var logLevel = configuration.GetValue("Logging:LogLevel:Default");
+ services.AddDbContext(builder =>
+ {
+ var dbConnString = configuration.GetValue("Kattbot:ConnectionString");
+ var logLevel = configuration.GetValue("Logging:LogLevel:Default");
- builder.EnableSensitiveDataLogging(logLevel == "Debug");
+ builder.EnableSensitiveDataLogging(logLevel == "Debug");
- builder.UseNpgsql(dbConnString);
- },
- ServiceLifetime.Transient,
- ServiceLifetime.Singleton);
+ builder.UseNpgsql(dbConnString);
+ });
}
private static void RegisterCommands(this DiscordClientBuilder builder, IConfiguration configuration)
@@ -95,28 +90,21 @@ private static void RegisterCommands(this DiscordClientBuilder builder, IConfigu
private static void RegisterEventHandlers(this DiscordClientBuilder builder)
{
- builder.ConfigureEventHandlers(
- cfg =>
- {
- cfg.HandleMessageCreated(
- (client, args) =>
- client.WriteNotification(new MessageCreatedNotification(args)));
- cfg.HandleMessageUpdated(
- (client, args) =>
- client.WriteNotification(new MessageUpdatedNotification(args)));
- cfg.HandleMessageDeleted(
- (client, args) =>
- client.WriteNotification(new MessageDeletedNotification(args)));
- cfg.HandleMessagesBulkDeleted(
- (client, args) =>
- client.WriteNotification(new MessageBulkDeletedNotification(args)));
- cfg.HandleMessageReactionAdded(
- (client, args) =>
- client.WriteNotification(new MessageReactionAddedNotification(args)));
- cfg.HandleMessageReactionRemoved(
- (client, args) =>
- client.WriteNotification(new MessageReactionRemovedNotification(args)));
- });
+ builder.ConfigureEventHandlers(cfg =>
+ {
+ cfg.HandleMessageCreated((client, args) =>
+ client.WriteNotification(new MessageCreatedNotification(args)));
+ cfg.HandleMessageUpdated((client, args) =>
+ client.WriteNotification(new MessageUpdatedNotification(args)));
+ cfg.HandleMessageDeleted((client, args) =>
+ client.WriteNotification(new MessageDeletedNotification(args)));
+ cfg.HandleMessagesBulkDeleted((client, args) =>
+ client.WriteNotification(new MessageBulkDeletedNotification(args)));
+ cfg.HandleMessageReactionAdded((client, args) =>
+ client.WriteNotification(new MessageReactionAddedNotification(args)));
+ cfg.HandleMessageReactionRemoved((client, args) =>
+ client.WriteNotification(new MessageReactionRemovedNotification(args)));
+ });
}
private static async Task WriteNotification(this DiscordClient client, T notification)
diff --git a/src/Kattbot/NotificationHandlers/Emotes/MessageCreatedNotificationHandler.cs b/src/Kattbot/NotificationHandlers/Emotes/MessageCreatedNotificationHandler.cs
index fd54015..07066b1 100644
--- a/src/Kattbot/NotificationHandlers/Emotes/MessageCreatedNotificationHandler.cs
+++ b/src/Kattbot/NotificationHandlers/Emotes/MessageCreatedNotificationHandler.cs
@@ -23,19 +23,16 @@ namespace Kattbot.NotificationHandlers.Emotes;
public class MessageCreatedNotificationHandler : BaseNotificationHandler,
INotificationHandler
{
- private readonly EmoteEntityBuilder _emoteBuilder;
private readonly EmotesRepository _kattbotRepo;
private readonly IOptions _botOptions;
private readonly ILogger _logger;
public MessageCreatedNotificationHandler(
ILogger logger,
- EmoteEntityBuilder emoteBuilder,
EmotesRepository kattbotRepo,
IOptions botOptions)
{
_logger = logger;
- _emoteBuilder = emoteBuilder;
_kattbotRepo = kattbotRepo;
_botOptions = botOptions;
}
@@ -64,7 +61,7 @@ public async Task Handle(MessageCreatedNotification notification, CancellationTo
ulong guildId = guild.Id;
- List emotes = _emoteBuilder.BuildFromSocketUserMessage(message, guildId);
+ List emotes = EmoteEntityBuilder.BuildFromSocketUserMessage(message, guildId);
if (emotes.Count > 0)
{
diff --git a/src/Kattbot/NotificationHandlers/Emotes/MessageReactionAddedNotificationHandler.cs b/src/Kattbot/NotificationHandlers/Emotes/MessageReactionAddedNotificationHandler.cs
index c243377..c2c8c7f 100644
--- a/src/Kattbot/NotificationHandlers/Emotes/MessageReactionAddedNotificationHandler.cs
+++ b/src/Kattbot/NotificationHandlers/Emotes/MessageReactionAddedNotificationHandler.cs
@@ -19,17 +19,14 @@ namespace Kattbot.NotificationHandlers.Emotes;
public class MessageReactionAddedNotificationHandler : BaseNotificationHandler,
INotificationHandler
{
- private readonly EmoteEntityBuilder _emoteBuilder;
private readonly EmotesRepository _kattbotRepo;
private readonly ILogger _logger;
public MessageReactionAddedNotificationHandler(
ILogger logger,
- EmoteEntityBuilder emoteBuilder,
EmotesRepository kattbotRepo)
{
_logger = logger;
- _emoteBuilder = emoteBuilder;
_kattbotRepo = kattbotRepo;
}
@@ -59,7 +56,7 @@ public Task Handle(MessageReactionAddedNotification notification, CancellationTo
return Task.CompletedTask;
}
- EmoteEntity emoteEntity = _emoteBuilder.BuildFromUserReaction(message, emoji, userId, guild.Id);
+ EmoteEntity emoteEntity = EmoteEntityBuilder.BuildFromUserReaction(message, emoji, userId, guild.Id);
_logger.LogDebug($"Saving reaction emote {emoteEntity}");
diff --git a/src/Kattbot/NotificationHandlers/Emotes/MessageReactionRemovedNotificationHandler.cs b/src/Kattbot/NotificationHandlers/Emotes/MessageReactionRemovedNotificationHandler.cs
index 5ce07af..d7d435d 100644
--- a/src/Kattbot/NotificationHandlers/Emotes/MessageReactionRemovedNotificationHandler.cs
+++ b/src/Kattbot/NotificationHandlers/Emotes/MessageReactionRemovedNotificationHandler.cs
@@ -19,17 +19,14 @@ namespace Kattbot.NotificationHandlers.Emotes;
public class MessageReactionRemovedNotificationHandler : BaseNotificationHandler,
INotificationHandler
{
- private readonly EmoteEntityBuilder _emoteBuilder;
private readonly EmotesRepository _kattbotRepo;
private readonly ILogger _logger;
public MessageReactionRemovedNotificationHandler(
ILogger logger,
- EmoteEntityBuilder emoteBuilder,
EmotesRepository kattbotRepo)
{
_logger = logger;
- _emoteBuilder = emoteBuilder;
_kattbotRepo = kattbotRepo;
}
@@ -59,7 +56,7 @@ public Task Handle(MessageReactionRemovedNotification notification, Cancellation
return Task.CompletedTask;
}
- EmoteEntity emoteEntity = _emoteBuilder.BuildFromUserReaction(message, emoji, userId, guild.Id);
+ EmoteEntity emoteEntity = EmoteEntityBuilder.BuildFromUserReaction(message, emoji, userId, guild.Id);
_logger.LogDebug($"Removing reaction emote {emoteEntity}");
diff --git a/src/Kattbot/NotificationHandlers/Emotes/MessageUpdatedNotificationHandler.cs b/src/Kattbot/NotificationHandlers/Emotes/MessageUpdatedNotificationHandler.cs
index 0e84994..b05bbc9 100644
--- a/src/Kattbot/NotificationHandlers/Emotes/MessageUpdatedNotificationHandler.cs
+++ b/src/Kattbot/NotificationHandlers/Emotes/MessageUpdatedNotificationHandler.cs
@@ -21,17 +21,14 @@ namespace Kattbot.NotificationHandlers.Emotes;
public class MessageUpdatedNotificationHandler : BaseNotificationHandler,
INotificationHandler
{
- private readonly EmoteEntityBuilder _emoteBuilder;
private readonly EmotesRepository _kattbotRepo;
private readonly ILogger _logger;
public MessageUpdatedNotificationHandler(
ILogger logger,
- EmoteEntityBuilder emoteBuilder,
EmotesRepository kattbotRepo)
{
_logger = logger;
- _emoteBuilder = emoteBuilder;
_kattbotRepo = kattbotRepo;
}
@@ -58,7 +55,7 @@ public async Task Handle(MessageUpdatedNotification notification, CancellationTo
await _kattbotRepo.RemoveEmotesForMessage(messageId);
- List emotes = _emoteBuilder.BuildFromSocketUserMessage(message, guildId);
+ List emotes = EmoteEntityBuilder.BuildFromSocketUserMessage(message, guildId);
if (emotes.Count > 0)
{
diff --git a/src/Kattbot/Program.cs b/src/Kattbot/Program.cs
index db0f07b..055685d 100644
--- a/src/Kattbot/Program.cs
+++ b/src/Kattbot/Program.cs
@@ -16,87 +16,80 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
-namespace Kattbot;
+HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
+ConfigurationManager configuration = builder.Configuration;
+IServiceCollection services = builder.Services;
-public class Program
+services.Configure(configuration.GetSection(BotOptions.OptionsKey));
+services.Configure(configuration.GetSection(KattGptOptions.OptionsKey));
+
+services.AddHttpClient();
+services.AddHttpClient();
+services.AddHttpClient();
+services.AddHttpClient();
+
+services.AddMediatR(cfg =>
+{
+ cfg.RegisterServicesFromAssemblyContaining();
+ cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(CommandRequestPipelineBehaviour<,>));
+});
+
+// Registered as Transient to match lifetime of MediatR
+services.AddTransient();
+
+AddWorkers(services);
+
+AddChannels(services);
+
+AddInternalServices(services);
+
+AddRepositories(services);
+
+services.AddDbContext(configuration);
+
+services.AddDiscordClient(configuration);
+
+IHost app = builder.Build();
+
+app.Run();
+
+return;
+
+static void AddInternalServices(IServiceCollection services)
{
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
-
- public static IHostBuilder CreateHostBuilder(string[] args)
- {
- return Host.CreateDefaultBuilder(args)
- .ConfigureServices((hostContext, services) =>
- {
- IConfiguration configuration = hostContext.Configuration;
-
- services.Configure(hostContext.Configuration.GetSection(BotOptions.OptionsKey));
- services.Configure(hostContext.Configuration.GetSection(KattGptOptions.OptionsKey));
-
- services.AddHttpClient();
- services.AddHttpClient();
- services.AddHttpClient();
- services.AddHttpClient();
-
- services.AddMediatR(cfg =>
- {
- cfg.RegisterServicesFromAssemblyContaining();
- cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(CommandRequestPipelineBehaviour<,>));
- });
- services.AddSingleton();
-
- services.AddSingleton();
- services.AddSingleton();
-
- AddWorkers(services);
-
- AddChannels(services);
-
- AddInternalServices(services);
-
- AddRepositories(services);
-
- services.AddDbContext(configuration);
-
- services.AddDiscordClient(configuration);
- });
- }
-
- private static void AddInternalServices(IServiceCollection services)
- {
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- }
-
- private static void AddRepositories(IServiceCollection services)
- {
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- }
-
- private static void AddWorkers(IServiceCollection services)
- {
- services.AddHostedService();
- services.AddHostedService();
- services.AddHostedService();
- services.AddHostedService();
- }
-
- private static void AddChannels(IServiceCollection services)
- {
- const int channelSize = 1024;
-
- services.AddSingleton(new CommandQueueChannel(Channel.CreateBounded(channelSize)));
- services.AddSingleton(new EventQueueChannel(Channel.CreateBounded(channelSize)));
- services.AddSingleton(new DiscordLogChannel(Channel.CreateBounded(channelSize)));
- }
+ services.AddSingleton();
+ services.AddSingleton();
+
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+}
+
+static void AddRepositories(IServiceCollection services)
+{
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+}
+
+static void AddWorkers(IServiceCollection services)
+{
+ services.AddHostedService();
+ services.AddHostedService();
+ services.AddHostedService();
+ services.AddHostedService();
+}
+
+static void AddChannels(IServiceCollection services)
+{
+ const int channelSize = 1024;
+
+ services.AddSingleton(new CommandQueueChannel(Channel.CreateBounded(channelSize)));
+ services.AddSingleton(new EventQueueChannel(Channel.CreateBounded(channelSize)));
+ services.AddSingleton(new DiscordLoggerChannel(Channel.CreateBounded(channelSize)));
}
diff --git a/src/Kattbot/Services/DiscordErrorLogger.cs b/src/Kattbot/Services/DiscordErrorLogger.cs
index 1f37a1d..e72095f 100644
--- a/src/Kattbot/Services/DiscordErrorLogger.cs
+++ b/src/Kattbot/Services/DiscordErrorLogger.cs
@@ -12,10 +12,10 @@ namespace Kattbot.Services;
public class DiscordErrorLogger
{
- private readonly DiscordLogChannel _channel;
+ private readonly DiscordLoggerChannel _channel;
private readonly BotOptions _options;
- public DiscordErrorLogger(IOptions options, DiscordLogChannel channel)
+ public DiscordErrorLogger(IOptions options, DiscordLoggerChannel channel)
{
_channel = channel;
_options = options.Value;
diff --git a/src/Kattbot/Services/EmoteEntityBuilder.cs b/src/Kattbot/Services/EmoteEntityBuilder.cs
index 929de87..f24ff8d 100644
--- a/src/Kattbot/Services/EmoteEntityBuilder.cs
+++ b/src/Kattbot/Services/EmoteEntityBuilder.cs
@@ -7,9 +7,9 @@
namespace Kattbot.Services;
-public class EmoteEntityBuilder
+public static class EmoteEntityBuilder
{
- public List BuildFromSocketUserMessage(DiscordMessage message, ulong guildId)
+ public static List BuildFromSocketUserMessage(DiscordMessage message, ulong guildId)
{
List emojiStrings = EmoteHelper.ExtractEmotesFromMessage(message.Content);
@@ -43,7 +43,7 @@ public List BuildFromSocketUserMessage(DiscordMessage message, ulon
return emotes;
}
- public EmoteEntity BuildFromUserReaction(DiscordMessage message, DiscordEmoji emote, ulong userId, ulong guildId)
+ public static EmoteEntity BuildFromUserReaction(DiscordMessage message, DiscordEmoji emote, ulong userId, ulong guildId)
{
var emoteEntity = new EmoteEntity
{
diff --git a/src/Kattbot/Workers/Channels.cs b/src/Kattbot/Workers/Channels.cs
index 0f3ea17..56be4f6 100644
--- a/src/Kattbot/Workers/Channels.cs
+++ b/src/Kattbot/Workers/Channels.cs
@@ -48,9 +48,9 @@ public record BaseDiscordLogItem(ulong DiscordGuildId, ulong DiscordChannelId);
public record DiscordLogItem(T Message, ulong DiscordGuildId, ulong DiscordChannelId)
: BaseDiscordLogItem(DiscordGuildId, DiscordChannelId);
-public class DiscordLogChannel : AbstractQueueChannel
+public class DiscordLoggerChannel : AbstractQueueChannel
{
- public DiscordLogChannel(Channel channel)
+ public DiscordLoggerChannel(Channel channel)
: base(channel)
{ }
}
diff --git a/src/Kattbot/Workers/CommandQueueWorker.cs b/src/Kattbot/Workers/CommandQueueWorker.cs
index f57839f..78aa0c2 100644
--- a/src/Kattbot/Workers/CommandQueueWorker.cs
+++ b/src/Kattbot/Workers/CommandQueueWorker.cs
@@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Kattbot.CommandHandlers;
using MediatR;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -11,14 +12,17 @@ namespace Kattbot.Workers;
public class CommandQueueWorker : BackgroundService
{
private readonly CommandQueueChannel _channel;
+ private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger _logger;
- private readonly IMediator _mediator;
- public CommandQueueWorker(ILogger logger, CommandQueueChannel channel, IMediator mediator)
+ public CommandQueueWorker(
+ ILogger logger,
+ CommandQueueChannel channel,
+ IServiceScopeFactory scopeFactory)
{
_logger = logger;
_channel = channel;
- _mediator = mediator;
+ _scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -35,7 +39,14 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
command.GetType().Name,
_channel.Reader.Count);
- _ = Task.Run(() => _mediator.Send(command, stoppingToken), stoppingToken);
+ _ = Task.Run(
+ async () =>
+ {
+ using var scope = _scopeFactory.CreateScope();
+ var mediator = scope.ServiceProvider.GetRequiredService();
+ await mediator.Send(command, stoppingToken);
+ },
+ stoppingToken);
}
}
catch (TaskCanceledException)
diff --git a/src/Kattbot/Workers/DiscordLoggerWorker.cs b/src/Kattbot/Workers/DiscordLoggerWorker.cs
index 6c8dec7..52bc2d9 100644
--- a/src/Kattbot/Workers/DiscordLoggerWorker.cs
+++ b/src/Kattbot/Workers/DiscordLoggerWorker.cs
@@ -12,14 +12,14 @@ namespace Kattbot.Workers;
public class DiscordLoggerWorker : BackgroundService
{
- private readonly DiscordLogChannel _channel;
+ private readonly DiscordLoggerChannel _channel;
private readonly DiscordClient _client;
private readonly ILogger _logger;
private readonly BotOptions _options;
public DiscordLoggerWorker(
ILogger logger,
- DiscordLogChannel channel,
+ DiscordLoggerChannel channel,
DiscordClient client,
IOptions options)
{
diff --git a/src/Kattbot/Workers/EventQueueWorker.cs b/src/Kattbot/Workers/EventQueueWorker.cs
index 19b4baf..93c95ae 100644
--- a/src/Kattbot/Workers/EventQueueWorker.cs
+++ b/src/Kattbot/Workers/EventQueueWorker.cs
@@ -5,6 +5,7 @@
using Kattbot.NotificationHandlers;
using Kattbot.Services;
using MediatR;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@@ -13,19 +14,19 @@ namespace Kattbot.Workers;
public class EventQueueWorker : BackgroundService
{
private readonly EventQueueChannel _channel;
+ private readonly IServiceScopeFactory _scopeFactory;
private readonly DiscordErrorLogger _discordErrorLogger;
private readonly ILogger _logger;
- private readonly NotificationPublisher _publisher;
public EventQueueWorker(
ILogger logger,
EventQueueChannel channel,
- NotificationPublisher publisher,
+ IServiceScopeFactory scopeFactory,
DiscordErrorLogger discordErrorLogger)
{
_logger = logger;
_channel = channel;
- _publisher = publisher;
+ _scopeFactory = scopeFactory;
_discordErrorLogger = discordErrorLogger;
}
@@ -39,7 +40,10 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
try
{
- await _publisher.Publish(notification, stoppingToken);
+ using var scope = _scopeFactory.CreateScope();
+ var publisher = scope.ServiceProvider.GetRequiredService();
+
+ await publisher.Publish(notification, stoppingToken);
}
catch (AggregateException ex)
{
diff --git a/src/Kattbot/appsettings.Development.json b/src/Kattbot/appsettings.Development.json
index 80c2600..b78fee8 100644
--- a/src/Kattbot/appsettings.Development.json
+++ b/src/Kattbot/appsettings.Development.json
@@ -9,7 +9,7 @@
},
"Kattbot": {
"ConnectionString": "Server=host.docker.internal:5433;Database=kattbot-dev;User Id=kattbot;Password=hunter2",
- "CommandPrefix": "!",
+ "CommandPrefix": ";;",
"AlternateCommandPrefix": "kd",
"ErrorLogGuildId": "1411437458062835809",
"ErrorLogChannelId": "1411441087997808750"
diff --git a/src/Kattbot/diag.Dockerfile b/src/Kattbot/diag.Dockerfile
index a3f5502..be1fb9a 100644
--- a/src/Kattbot/diag.Dockerfile
+++ b/src/Kattbot/diag.Dockerfile
@@ -4,6 +4,7 @@ RUN dotnet tool install -g dotnet-counters && \
dotnet tool install -g dotnet-monitor && \
dotnet tool install -g dotnet-trace && \
dotnet tool install -g dotnet-dump && \
+ dotnet tool install -g dotnet-gcdump && \
dotnet tool install -g dotnet-stack
ENV PATH="/root/.dotnet/tools:$PATH"