diff --git a/PluralKit.Bot/Bot.cs b/PluralKit.Bot/Bot.cs index 73a61949..b4d6632b 100644 --- a/PluralKit.Bot/Bot.cs +++ b/PluralKit.Bot/Bot.cs @@ -9,8 +9,6 @@ using App.Metrics; using Autofac; -using Dapper; - using DSharpPlus; using DSharpPlus.Entities; using DSharpPlus.EventArgs; @@ -24,7 +22,6 @@ using Sentry; using Serilog; using Serilog.Context; -using Serilog.Events; namespace PluralKit.Bot { @@ -55,9 +52,6 @@ namespace PluralKit.Bot public void Init() { - // Attach the handlers we need - _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 @@ -68,12 +62,12 @@ namespace PluralKit.Bot _client.MessageReactionAdded += HandleEvent; // Update shard status for shards immediately on connect - _client.Ready += args => + _client.Ready += (client, _) => { _hasReceivedReady = true; - return UpdateBotStatus(args.Client); + return UpdateBotStatus(client); }; - _client.Resumed += args => UpdateBotStatus(args.Client); + _client.Resumed += (client, _) => UpdateBotStatus(client); // Init the shard stuff _services.Resolve().Init(); @@ -101,7 +95,7 @@ namespace PluralKit.Bot await _client.UpdateStatusAsync(new DiscordActivity("Restarting... (please wait)"), UserStatus.Idle); } - private Task HandleEvent(T evt) where T: DiscordEventArgs + private Task HandleEvent(DiscordClient shard, T evt) where T: DiscordEventArgs { // We don't want to stall the event pipeline, so we'll "fork" inside here var _ = HandleEventInner(); @@ -118,7 +112,7 @@ namespace PluralKit.Bot // 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); + sentryEnricher?.Enrich(serviceScope.Resolve(), shard, evt); // Find an event handler that can handle the type of event () we're given var handler = serviceScope.Resolve>(); @@ -129,7 +123,7 @@ namespace PluralKit.Bot // the TryHandle call returns true if it's handled the event // Usually it won't, so just pass it on to the main handler if (queue == null || !await queue.TryHandle(evt)) - await handler.Handle(evt); + await handler.Handle(shard, evt); } catch (Exception exc) { @@ -209,23 +203,5 @@ namespace PluralKit.Bot } catch (WebSocketException) { } } - - public void FrameworkLog(object sender, DebugLogMessageEventArgs args) - { - // Bridge D#+ logging to Serilog - LogEventLevel level = LogEventLevel.Verbose; - if (args.Level == LogLevel.Critical) - level = LogEventLevel.Fatal; - else if (args.Level == LogLevel.Debug) - level = LogEventLevel.Debug; - else if (args.Level == LogLevel.Error) - level = LogEventLevel.Error; - else if (args.Level == LogLevel.Info) - level = LogEventLevel.Information; - else if (args.Level == LogLevel.Warning) - level = LogEventLevel.Warning; - - _logger.Write(level, args.Exception, "D#+ {Source}: {Message}", args.Application, args.Message); - } } } \ No newline at end of file diff --git a/PluralKit.Bot/Handlers/IEventHandler.cs b/PluralKit.Bot/Handlers/IEventHandler.cs index c23dc09b..839eeba0 100644 --- a/PluralKit.Bot/Handlers/IEventHandler.cs +++ b/PluralKit.Bot/Handlers/IEventHandler.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; +using DSharpPlus; using DSharpPlus.Entities; using DSharpPlus.EventArgs; @@ -7,7 +8,7 @@ namespace PluralKit.Bot { public interface IEventHandler where T: DiscordEventArgs { - Task Handle(T evt); + Task Handle(DiscordClient shard, T evt); DiscordChannel ErrorChannelFor(T evt) => null; } diff --git a/PluralKit.Bot/Handlers/MessageCreated.cs b/PluralKit.Bot/Handlers/MessageCreated.cs index 2bb13201..6046c65c 100644 --- a/PluralKit.Bot/Handlers/MessageCreated.cs +++ b/PluralKit.Bot/Handlers/MessageCreated.cs @@ -48,7 +48,7 @@ namespace PluralKit.Bot // We consider a message duplicate if it has the same ID as the previous message that hit the gateway _lastMessageCache.GetLastMessage(evt.ChannelId) == evt.Id; - public async Task Handle(MessageCreateEventArgs evt) + public async Task Handle(DiscordClient shard, MessageCreateEventArgs evt) { if (evt.Author?.Id == _client.CurrentUser?.Id) return; if (evt.Message.MessageType != MessageType.Default) return; @@ -71,7 +71,7 @@ namespace PluralKit.Bot // Only do command/proxy handling if it's a user account if (evt.Message.Author.IsBot || evt.Message.WebhookMessage || evt.Message.Author.IsSystem == true) return; - if (await TryHandleCommand(evt, ctx)) + if (await TryHandleCommand(shard, evt, ctx)) return; await TryHandleProxy(evt, ctx); } @@ -85,7 +85,7 @@ namespace PluralKit.Bot return true; } - private async ValueTask TryHandleCommand(MessageCreateEventArgs evt, MessageContext ctx) + private async ValueTask TryHandleCommand(DiscordClient shard, MessageCreateEventArgs evt, MessageContext ctx) { var content = evt.Message.Content; if (content == null) return false; @@ -102,7 +102,7 @@ namespace PluralKit.Bot try { var system = ctx.SystemId != null ? await _db.Execute(c => _repo.GetSystem(c, ctx.SystemId.Value)) : null; - await _tree.ExecuteCommand(new Context(_services, evt.Client, evt.Message, cmdStart, system, ctx)); + await _tree.ExecuteCommand(new Context(_services, shard, evt.Message, cmdStart, system, ctx)); } catch (PKError) { diff --git a/PluralKit.Bot/Handlers/MessageDeleted.cs b/PluralKit.Bot/Handlers/MessageDeleted.cs index 6869fa0b..205ddbc2 100644 --- a/PluralKit.Bot/Handlers/MessageDeleted.cs +++ b/PluralKit.Bot/Handlers/MessageDeleted.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading.Tasks; +using DSharpPlus; using DSharpPlus.EventArgs; using PluralKit.Core; @@ -23,14 +24,14 @@ namespace PluralKit.Bot _logger = logger.ForContext(); } - public async Task Handle(MessageDeleteEventArgs evt) + public async Task Handle(DiscordClient shard, MessageDeleteEventArgs evt) { // Delete deleted webhook messages from the data store // Most of the data in the given message is wrong/missing, so always delete just to be sure. await _db.Execute(c => _repo.DeleteMessage(c, evt.Message.Id)); } - public async Task Handle(MessageBulkDeleteEventArgs evt) + public async Task Handle(DiscordClient shard, MessageBulkDeleteEventArgs evt) { // Same as above, but bulk _logger.Information("Bulk deleting {Count} messages in channel {Channel}", evt.Messages.Count, evt.Channel.Id); diff --git a/PluralKit.Bot/Handlers/MessageEdited.cs b/PluralKit.Bot/Handlers/MessageEdited.cs index a407f2dc..c0b31c38 100644 --- a/PluralKit.Bot/Handlers/MessageEdited.cs +++ b/PluralKit.Bot/Handlers/MessageEdited.cs @@ -29,7 +29,7 @@ namespace PluralKit.Bot _client = client; } - public async Task Handle(MessageUpdateEventArgs evt) + public async Task Handle(DiscordClient shard, MessageUpdateEventArgs evt) { if (evt.Author?.Id == _client.CurrentUser?.Id) return; diff --git a/PluralKit.Bot/Handlers/ReactionAdded.cs b/PluralKit.Bot/Handlers/ReactionAdded.cs index a9abbbf6..b3f29ea4 100644 --- a/PluralKit.Bot/Handlers/ReactionAdded.cs +++ b/PluralKit.Bot/Handlers/ReactionAdded.cs @@ -1,4 +1,3 @@ -using System; using System.Threading.Tasks; using DSharpPlus; @@ -29,18 +28,18 @@ namespace PluralKit.Bot _logger = logger.ForContext(); } - public async Task Handle(MessageReactionAddEventArgs evt) + public async Task Handle(DiscordClient shard, MessageReactionAddEventArgs evt) { - await TryHandleProxyMessageReactions(evt); + await TryHandleProxyMessageReactions(shard, evt); } - private async ValueTask TryHandleProxyMessageReactions(MessageReactionAddEventArgs evt) + private async ValueTask TryHandleProxyMessageReactions(DiscordClient shard, MessageReactionAddEventArgs evt) { // Sometimes we get events from users that aren't in the user cache // In that case we get a "broken" user object (where eg. calling IsBot throws an exception) // We just ignore all of those for now, should be quite rare... - if (!evt.Client.TryGetCachedUser(evt.User.Id, out _)) return; + if (!shard.TryGetCachedUser(evt.User.Id, out _)) return; // check if it's a command message first // since this can happen in DMs as well @@ -79,7 +78,7 @@ namespace PluralKit.Bot await using var conn = await _db.Obtain(); var msg = await _repo.GetMessage(conn, evt.Message.Id); if (msg != null) - await HandleQueryReaction(evt, msg); + await HandleQueryReaction(shard, evt, msg); break; } @@ -139,14 +138,14 @@ namespace PluralKit.Bot // No need to delete database row here, it'll get deleted by the once-per-minute scheduled task. } - private async ValueTask HandleQueryReaction(MessageReactionAddEventArgs evt, FullMessage msg) + private async ValueTask HandleQueryReaction(DiscordClient shard, MessageReactionAddEventArgs evt, FullMessage msg) { // Try to DM the user info about the message var member = await evt.Guild.GetMember(evt.User.Id); try { await member.SendMessageAsync(embed: await _embeds.CreateMemberEmbed(msg.System, msg.Member, evt.Guild, LookupContext.ByNonOwner)); - await member.SendMessageAsync(embed: await _embeds.CreateMessageInfoEmbed(evt.Client, msg)); + await member.SendMessageAsync(embed: await _embeds.CreateMessageInfoEmbed(shard, msg)); } catch (UnauthorizedException) { } // No permissions to DM, can't check for this :( diff --git a/PluralKit.Bot/Modules.cs b/PluralKit.Bot/Modules.cs index a6752cbc..5841149f 100644 --- a/PluralKit.Bot/Modules.cs +++ b/PluralKit.Bot/Modules.cs @@ -24,7 +24,8 @@ namespace PluralKit.Bot Token = c.Resolve().Token, TokenType = TokenType.Bot, MessageCacheSize = 0, - LargeThreshold = 50 + LargeThreshold = 50, + LoggerFactory = c.Resolve() }).AsSelf(); builder.Register(c => new DiscordShardedClient(c.Resolve())).AsSelf().SingleInstance(); builder.Register(c => new DiscordRestClient(c.Resolve())).AsSelf().SingleInstance(); diff --git a/PluralKit.Bot/PluralKit.Bot.csproj b/PluralKit.Bot/PluralKit.Bot.csproj index 1c6a5e1d..74473bc1 100644 --- a/PluralKit.Bot/PluralKit.Bot.csproj +++ b/PluralKit.Bot/PluralKit.Bot.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/PluralKit.Bot/Services/ShardInfoService.cs b/PluralKit.Bot/Services/ShardInfoService.cs index 6214f66b..7bbbe2a1 100644 --- a/PluralKit.Bot/Services/ShardInfoService.cs +++ b/PluralKit.Bot/Services/ShardInfoService.cs @@ -42,7 +42,7 @@ namespace PluralKit.Bot { // We initialize this before any shards are actually created and connected // This means the client won't know the shard count, so we attach a listener every time a shard gets connected - _client.SocketOpened += RefreshShardList; + _client.SocketOpened += (_, __) => RefreshShardList(); } private void ReportShardStatus() @@ -66,8 +66,8 @@ namespace PluralKit.Bot // Call our own SocketOpened listener manually (and then attach the listener properly) - await SocketOpened(shard); - shard.SocketOpened += () => SocketOpened(shard); + await SocketOpened(shard, null); + shard.SocketOpened += SocketOpened; // Register listeners for new shards _logger.Information("Attaching listeners to new shard #{Shard}", shard.ShardId); @@ -81,11 +81,11 @@ namespace PluralKit.Bot } } - private Task SocketOpened(DiscordClient e) + private Task SocketOpened(DiscordClient shard, SocketEventArgs _) { // We do nothing else here, since this kinda doesn't mean *much*? It's only really started once we get Ready/Resumed // And it doesn't get fired first time around since we don't have time to add the event listener before it's fired' - _logger.Information("Shard #{Shard} opened socket", e.ShardId); + _logger.Information("Shard #{Shard} opened socket", shard.ShardId); return Task.CompletedTask; } @@ -99,45 +99,45 @@ namespace PluralKit.Bot return info; } - private Task Resumed(ReadyEventArgs e) + private Task Resumed(DiscordClient shard, ReadyEventArgs e) { - _logger.Information("Shard #{Shard} resumed connection", e.Client.ShardId); + _logger.Information("Shard #{Shard} resumed connection", shard.ShardId); - var info = TryGetShard(e.Client); + var info = TryGetShard(shard); // info.LastConnectionTime = SystemClock.Instance.GetCurrentInstant(); info.Connected = true; ReportShardStatus(); return Task.CompletedTask; } - private Task Ready(ReadyEventArgs e) + private Task Ready(DiscordClient shard, ReadyEventArgs e) { - _logger.Information("Shard #{Shard} sent Ready event", e.Client.ShardId); + _logger.Information("Shard #{Shard} sent Ready event", shard.ShardId); - var info = TryGetShard(e.Client); + var info = TryGetShard(shard); info.LastConnectionTime = SystemClock.Instance.GetCurrentInstant(); info.Connected = true; ReportShardStatus(); return Task.CompletedTask; } - private Task SocketClosed(SocketCloseEventArgs e) + private Task SocketClosed(DiscordClient shard, SocketCloseEventArgs e) { - _logger.Warning("Shard #{Shard} disconnected ({CloseCode}: {CloseMessage})", e.Client.ShardId, e.CloseCode, e.CloseMessage); + _logger.Warning("Shard #{Shard} disconnected ({CloseCode}: {CloseMessage})", shard.ShardId, e.CloseCode, e.CloseMessage); - var info = TryGetShard(e.Client); + var info = TryGetShard(shard); info.DisconnectionCount++; info.Connected = false; ReportShardStatus(); return Task.CompletedTask; } - private Task Heartbeated(HeartbeatEventArgs e) + private Task Heartbeated(DiscordClient shard, HeartbeatEventArgs e) { var latency = Duration.FromMilliseconds(e.Ping); - _logger.Information("Shard #{Shard} received heartbeat (latency: {Latency} ms)", e.Client.ShardId, latency.Milliseconds); + _logger.Information("Shard #{Shard} received heartbeat (latency: {Latency} ms)", shard.ShardId, latency.Milliseconds); - var info = TryGetShard(e.Client); + var info = TryGetShard(shard); info.LastHeartbeatTime = e.Timestamp.ToInstant(); info.Connected = true; info.ShardLatency = latency; diff --git a/PluralKit.Bot/Tracing/EventDestructuring.cs b/PluralKit.Bot/Tracing/EventDestructuring.cs index d302cd54..fcc655bf 100644 --- a/PluralKit.Bot/Tracing/EventDestructuring.cs +++ b/PluralKit.Bot/Tracing/EventDestructuring.cs @@ -48,7 +48,8 @@ namespace PluralKit.Bot } // Want shard last, just for visual reasons - props.Add(new LogEventProperty("Shard", new ScalarValue(dea.Client.ShardId))); + // TODO: D#+ update means we can't pull shard ID out of this, what do? + // props.Add(new LogEventProperty("Shard", new ScalarValue(dea.Client.ShardId))); result = new StructureValue(props); return true; diff --git a/PluralKit.Bot/Utils/ContextUtils.cs b/PluralKit.Bot/Utils/ContextUtils.cs index 855ae283..4733bd99 100644 --- a/PluralKit.Bot/Utils/ContextUtils.cs +++ b/PluralKit.Bot/Utils/ContextUtils.cs @@ -79,7 +79,7 @@ namespace PluralKit.Bot { public static async Task AwaitReaction(this Context ctx, DiscordMessage message, DiscordUser user = null, Func predicate = null, TimeSpan? timeout = null) { var tcs = new TaskCompletionSource(); - Task Inner(MessageReactionAddEventArgs args) { + Task Inner(DiscordClient _, MessageReactionAddEventArgs args) { if (message.Id != args.Message.Id) return Task.CompletedTask; // Ignore reactions for different messages if (user != null && user.Id != args.User.Id) return Task.CompletedTask; // Ignore messages from other users if a user was defined if (predicate != null && !predicate.Invoke(args)) return Task.CompletedTask; // Check predicate diff --git a/PluralKit.Bot/Utils/SentryUtils.cs b/PluralKit.Bot/Utils/SentryUtils.cs index e7f46396..7b11f876 100644 --- a/PluralKit.Bot/Utils/SentryUtils.cs +++ b/PluralKit.Bot/Utils/SentryUtils.cs @@ -10,7 +10,7 @@ namespace PluralKit.Bot { public interface ISentryEnricher where T: DiscordEventArgs { - void Enrich(Scope scope, T evt); + void Enrich(Scope scope, DiscordClient shard, T evt); } public class SentryEnricher: @@ -23,7 +23,7 @@ namespace PluralKit.Bot // 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) + public void Enrich(Scope scope, DiscordClient shard, MessageCreateEventArgs evt) { scope.AddBreadcrumb(evt.Message.Content, "event.message", data: new Dictionary { @@ -32,7 +32,7 @@ namespace PluralKit.Bot {"guild", evt.Channel.GuildId.ToString()}, {"message", evt.Message.Id.ToString()}, }); - scope.SetTag("shard", evt.Client.ShardId.ToString()); + scope.SetTag("shard", shard.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 @@ -40,7 +40,7 @@ namespace PluralKit.Bot scope.AddBreadcrumb(perms.ToPermissionString(), "permissions"); } - public void Enrich(Scope scope, MessageDeleteEventArgs evt) + public void Enrich(Scope scope, DiscordClient shard, MessageDeleteEventArgs evt) { scope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary() @@ -49,10 +49,10 @@ namespace PluralKit.Bot {"guild", evt.Channel.GuildId.ToString()}, {"message", evt.Message.Id.ToString()}, }); - scope.SetTag("shard", evt.Client.ShardId.ToString()); + scope.SetTag("shard", shard.ShardId.ToString()); } - public void Enrich(Scope scope, MessageUpdateEventArgs evt) + public void Enrich(Scope scope, DiscordClient shard, MessageUpdateEventArgs evt) { scope.AddBreadcrumb(evt.Message.Content ?? "", "event.messageEdit", data: new Dictionary() @@ -61,10 +61,10 @@ namespace PluralKit.Bot {"guild", evt.Channel.GuildId.ToString()}, {"message", evt.Message.Id.ToString()} }); - scope.SetTag("shard", evt.Client.ShardId.ToString()); + scope.SetTag("shard", shard.ShardId.ToString()); } - public void Enrich(Scope scope, MessageBulkDeleteEventArgs evt) + public void Enrich(Scope scope, DiscordClient shard, MessageBulkDeleteEventArgs evt) { scope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary() @@ -73,10 +73,10 @@ namespace PluralKit.Bot {"guild", evt.Channel.Id.ToString()}, {"messages", string.Join(",", evt.Messages.Select(m => m.Id))}, }); - scope.SetTag("shard", evt.Client.ShardId.ToString()); + scope.SetTag("shard", shard.ShardId.ToString()); } - public void Enrich(Scope scope, MessageReactionAddEventArgs evt) + public void Enrich(Scope scope, DiscordClient shard, MessageReactionAddEventArgs evt) { scope.AddBreadcrumb("", "event.reaction", data: new Dictionary() @@ -87,7 +87,7 @@ namespace PluralKit.Bot {"message", evt.Message.Id.ToString()}, {"reaction", evt.Emoji.Name} }); - scope.SetTag("shard", evt.Client.ShardId.ToString()); + scope.SetTag("shard", shard.ShardId.ToString()); } } } \ No newline at end of file diff --git a/PluralKit.Core/Modules.cs b/PluralKit.Core/Modules.cs index 6673f6e0..08dffd8c 100644 --- a/PluralKit.Core/Modules.cs +++ b/PluralKit.Core/Modules.cs @@ -99,6 +99,10 @@ namespace PluralKit.Core // AutoActivate ensures logging is enabled as early as possible in the API startup flow // since we set the Log.Logger global >.> .AutoActivate(); + + builder.Register(c => new Microsoft.Extensions.Logging.LoggerFactory().AddSerilog(c.Resolve())) + .As() + .SingleInstance(); } private ILogger InitLogger(CoreConfig config) @@ -111,9 +115,10 @@ namespace PluralKit.Core .ConfigureForNodaTime(DateTimeZoneProviders.Tzdb) .Enrich.WithProperty("Component", _component) .MinimumLevel.Is(config.ConsoleLogLevel) - - // Don't want App.Metrics spam + + // Don't want App.Metrics/D#+ spam .MinimumLevel.Override("App.Metrics", LogEventLevel.Information) + .MinimumLevel.Override("DSharpPlus", LogEventLevel.Debug) // Actual formatting for these is handled in ScalarFormatting .Destructure.AsScalar() diff --git a/PluralKit.Core/PluralKit.Core.csproj b/PluralKit.Core/PluralKit.Core.csproj index 4ba1ac6b..a9dbc751 100644 --- a/PluralKit.Core/PluralKit.Core.csproj +++ b/PluralKit.Core/PluralKit.Core.csproj @@ -17,18 +17,20 @@ - - - - - - + + + + + + + +