diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index 05e24126..ec4a1d32 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -47,6 +47,8 @@ namespace PluralKit.Bot _client.DebugLogger.LogMessageReceived += FrameworkLog; // HandleEvent takes a type parameter, automatically inferred by the event type + // It will then look up an IEventHandler in the DI container and call that object's handler method + // For registering new ones, see Modules.cs _client.MessageCreated += HandleEvent; _client.MessageDeleted += HandleEvent; _client.MessageUpdated += HandleEvent; @@ -69,8 +71,14 @@ namespace PluralKit.Bot async Task HandleEventInner() { var serviceScope = _services.BeginLifetimeScope(); + + // Find an event handler that can handle the type of event () we're given var handler = serviceScope.Resolve>(); - + + // Also, find a Sentry enricher for the event type (if one is present), and ask it to put some event data in the Sentry scope + var sentryEnricher = serviceScope.ResolveOptional>(); + sentryEnricher?.Enrich(serviceScope.Resolve(), evt); + try { await handler.Handle(evt); diff --git a/PluralKit.Bot/Handlers/MessageCreated.cs b/PluralKit.Bot/Handlers/MessageCreated.cs index 955b592b..a46b1058 100644 --- a/PluralKit.Bot/Handlers/MessageCreated.cs +++ b/PluralKit.Bot/Handlers/MessageCreated.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using App.Metrics; @@ -129,21 +128,6 @@ namespace PluralKit.Bot { _metrics.Measure.Meter.Mark(BotMetrics.MessagesReceived); _lastMessageCache.AddMessage(evt.Channel.Id, evt.Message.Id); - - // Add message info as Sentry breadcrumb - _sentryScope.AddBreadcrumb(evt.Message.Content, "event.message", data: new Dictionary - { - {"user", evt.Author.Id.ToString()}, - {"channel", evt.Channel.Id.ToString()}, - {"guild", evt.Channel.GuildId.ToString()}, - {"message", evt.Message.Id.ToString()}, - }); - _sentryScope.SetTag("shard", evt.Client.ShardId.ToString()); - - // Also report information about the bot's permissions in the channel - // We get a lot of permission errors so this'll be useful for determining problems - var perms = evt.Channel.BotPermissions(); - _sentryScope.AddBreadcrumb(perms.ToPermissionString(), "permissions"); } } } \ No newline at end of file diff --git a/PluralKit.Bot/Handlers/MessageDeleted.cs b/PluralKit.Bot/Handlers/MessageDeleted.cs index ca1be0cc..f98e143c 100644 --- a/PluralKit.Bot/Handlers/MessageDeleted.cs +++ b/PluralKit.Bot/Handlers/MessageDeleted.cs @@ -12,37 +12,19 @@ namespace PluralKit.Bot public class MessageDeleted: IEventHandler, IEventHandler { private readonly ProxyService _proxy; - private readonly Scope _sentryScope; - public MessageDeleted(Scope sentryScope, ProxyService proxy) + public MessageDeleted(ProxyService proxy) { - _sentryScope = sentryScope; _proxy = proxy; } public Task Handle(MessageDeleteEventArgs evt) { - _sentryScope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary() - { - {"channel", evt.Channel.Id.ToString()}, - {"guild", evt.Channel.GuildId.ToString()}, - {"message", evt.Message.Id.ToString()}, - }); - _sentryScope.SetTag("shard", evt.Client.ShardId.ToString()); - return _proxy.HandleMessageDeletedAsync(evt); } public Task Handle(MessageBulkDeleteEventArgs evt) { - _sentryScope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary() - { - {"channel", evt.Channel.Id.ToString()}, - {"guild", evt.Channel.Id.ToString()}, - {"messages", string.Join(",", evt.Messages.Select(m => m.Id))}, - }); - _sentryScope.SetTag("shard", evt.Client.ShardId.ToString()); - return _proxy.HandleMessageBulkDeleteAsync(evt); } } diff --git a/PluralKit.Bot/Handlers/MessageEdited.cs b/PluralKit.Bot/Handlers/MessageEdited.cs index 995c82bf..551ecd05 100644 --- a/PluralKit.Bot/Handlers/MessageEdited.cs +++ b/PluralKit.Bot/Handlers/MessageEdited.cs @@ -35,14 +35,6 @@ namespace PluralKit.Bot // Also, if this is in DMs don't bother either if (evt.Channel.Guild == null) return; - - _sentryScope.AddBreadcrumb(evt.Message.Content ?? "", "event.messageEdit", data: new Dictionary() - { - {"channel", evt.Channel.Id.ToString()}, - {"guild", evt.Channel.GuildId.ToString()}, - {"message", evt.Message.Id.ToString()} - }); - _sentryScope.SetTag("shard", evt.Client.ShardId.ToString()); // If this isn't the last message in the channel, don't do anything if (_lastMessageCache.GetLastMessage(evt.Channel.Id) != evt.Message.Id) return; diff --git a/PluralKit.Bot/Handlers/ReactionAdded.cs b/PluralKit.Bot/Handlers/ReactionAdded.cs index 5a064d3f..a87e6771 100644 --- a/PluralKit.Bot/Handlers/ReactionAdded.cs +++ b/PluralKit.Bot/Handlers/ReactionAdded.cs @@ -10,25 +10,14 @@ namespace PluralKit.Bot public class ReactionAdded: IEventHandler { private readonly ProxyService _proxy; - private readonly Scope _sentryScope; - public ReactionAdded(ProxyService proxy, Scope sentryScope) + public ReactionAdded(ProxyService proxy) { _proxy = proxy; - _sentryScope = sentryScope; } public Task Handle(MessageReactionAddEventArgs evt) { - _sentryScope.AddBreadcrumb("", "event.reaction", data: new Dictionary() - { - {"user", evt.User.Id.ToString()}, - {"channel", (evt.Channel?.Id ?? 0).ToString()}, - {"guild", (evt.Channel?.GuildId ?? 0).ToString()}, - {"message", evt.Message.Id.ToString()}, - {"reaction", evt.Emoji.Name} - }); - _sentryScope.SetTag("shard", evt.Client.ShardId.ToString()); return _proxy.HandleReactionAddedAsync(evt); } } diff --git a/PluralKit.Bot/Modules.cs b/PluralKit.Bot/Modules.cs index f06f922f..4de422cc 100644 --- a/PluralKit.Bot/Modules.cs +++ b/PluralKit.Bot/Modules.cs @@ -69,6 +69,13 @@ namespace PluralKit.Bot // Sentry stuff builder.Register(_ => new Scope(null)).AsSelf().InstancePerLifetimeScope(); + builder.RegisterType() + .As>() + .As>() + .As>() + .As>() + .As>() + .SingleInstance(); // Utils builder.Register(c => new HttpClient diff --git a/PluralKit.Bot/Utils/SentryUtils.cs b/PluralKit.Bot/Utils/SentryUtils.cs new file mode 100644 index 00000000..e7f46396 --- /dev/null +++ b/PluralKit.Bot/Utils/SentryUtils.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.Linq; + +using DSharpPlus; +using DSharpPlus.EventArgs; + +using Sentry; + +namespace PluralKit.Bot +{ + public interface ISentryEnricher where T: DiscordEventArgs + { + void Enrich(Scope scope, T evt); + } + + public class SentryEnricher: + ISentryEnricher, + ISentryEnricher, + ISentryEnricher, + ISentryEnricher, + ISentryEnricher + { + // TODO: should this class take the Scope by dependency injection instead? + // Would allow us to create a centralized "chain of handlers" where this class could just be registered as an entry in + + public void Enrich(Scope scope, MessageCreateEventArgs evt) + { + scope.AddBreadcrumb(evt.Message.Content, "event.message", data: new Dictionary + { + {"user", evt.Author.Id.ToString()}, + {"channel", evt.Channel.Id.ToString()}, + {"guild", evt.Channel.GuildId.ToString()}, + {"message", evt.Message.Id.ToString()}, + }); + scope.SetTag("shard", evt.Client.ShardId.ToString()); + + // Also report information about the bot's permissions in the channel + // We get a lot of permission errors so this'll be useful for determining problems + var perms = evt.Channel.BotPermissions(); + scope.AddBreadcrumb(perms.ToPermissionString(), "permissions"); + } + + public void Enrich(Scope scope, MessageDeleteEventArgs evt) + { + scope.AddBreadcrumb("", "event.messageDelete", + data: new Dictionary() + { + {"channel", evt.Channel.Id.ToString()}, + {"guild", evt.Channel.GuildId.ToString()}, + {"message", evt.Message.Id.ToString()}, + }); + scope.SetTag("shard", evt.Client.ShardId.ToString()); + } + + public void Enrich(Scope scope, MessageUpdateEventArgs evt) + { + scope.AddBreadcrumb(evt.Message.Content ?? "", "event.messageEdit", + data: new Dictionary() + { + {"channel", evt.Channel.Id.ToString()}, + {"guild", evt.Channel.GuildId.ToString()}, + {"message", evt.Message.Id.ToString()} + }); + scope.SetTag("shard", evt.Client.ShardId.ToString()); + } + + public void Enrich(Scope scope, MessageBulkDeleteEventArgs evt) + { + scope.AddBreadcrumb("", "event.messageDelete", + data: new Dictionary() + { + {"channel", evt.Channel.Id.ToString()}, + {"guild", evt.Channel.Id.ToString()}, + {"messages", string.Join(",", evt.Messages.Select(m => m.Id))}, + }); + scope.SetTag("shard", evt.Client.ShardId.ToString()); + } + + public void Enrich(Scope scope, MessageReactionAddEventArgs evt) + { + scope.AddBreadcrumb("", "event.reaction", + data: new Dictionary() + { + {"user", evt.User.Id.ToString()}, + {"channel", (evt.Channel?.Id ?? 0).ToString()}, + {"guild", (evt.Channel?.GuildId ?? 0).ToString()}, + {"message", evt.Message.Id.ToString()}, + {"reaction", evt.Emoji.Name} + }); + scope.SetTag("shard", evt.Client.ShardId.ToString()); + } + } +} \ No newline at end of file