Migrate DI container to Autofac
This commit is contained in:
parent
4311cb3ad1
commit
1ea0526ef8
12
PluralKit.API/Modules.cs
Normal file
12
PluralKit.API/Modules.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using Autofac;
|
||||||
|
|
||||||
|
namespace PluralKit.API
|
||||||
|
{
|
||||||
|
public class APIModule: Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
builder.RegisterType<TokenAuthService>().AsSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
using Microsoft.AspNetCore;
|
using Autofac.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
namespace PluralKit.API
|
namespace PluralKit.API
|
||||||
{
|
{
|
||||||
@ -8,13 +11,16 @@ namespace PluralKit.API
|
|||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
InitUtils.Init();
|
InitUtils.Init();
|
||||||
CreateWebHostBuilder(args).Build().Run();
|
CreateHostBuilder(args).Build().Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
WebHost.CreateDefaultBuilder(args)
|
Host.CreateDefaultBuilder(args)
|
||||||
.UseConfiguration(InitUtils.BuildConfiguration(args).Build())
|
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||||
.ConfigureKestrel(opts => { opts.ListenAnyIP(5000);})
|
.ConfigureWebHostDefaults(whb => whb
|
||||||
.UseStartup<Startup>();
|
|
||||||
|
.UseConfiguration(InitUtils.BuildConfiguration(args).Build())
|
||||||
|
.ConfigureKestrel(opts => { opts.ListenAnyIP(5000); })
|
||||||
|
.UseStartup<Startup>());
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,10 +1,14 @@
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Autofac;
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
using PluralKit.Core;
|
||||||
|
|
||||||
namespace PluralKit.API
|
namespace PluralKit.API
|
||||||
{
|
{
|
||||||
public class Startup
|
public class Startup
|
||||||
@ -23,20 +27,16 @@ namespace PluralKit.API
|
|||||||
services.AddControllers()
|
services.AddControllers()
|
||||||
.SetCompatibilityVersion(CompatibilityVersion.Latest)
|
.SetCompatibilityVersion(CompatibilityVersion.Latest)
|
||||||
.AddNewtonsoftJson(); // sorry MS, this just does *more*
|
.AddNewtonsoftJson(); // sorry MS, this just does *more*
|
||||||
|
}
|
||||||
|
|
||||||
services
|
public void ConfigureContainer(ContainerBuilder builder)
|
||||||
.AddTransient<IDataStore, PostgresDataStore>()
|
{
|
||||||
.AddSingleton<SchemaService>()
|
builder.RegisterInstance(Configuration);
|
||||||
|
builder.RegisterModule(new ConfigModule<object>());
|
||||||
.AddSingleton(svc => InitUtils.InitMetrics(svc.GetRequiredService<CoreConfig>(), "API"))
|
builder.RegisterModule(new LoggingModule("api"));
|
||||||
|
builder.RegisterModule(new MetricsModule("API"));
|
||||||
.AddScoped<TokenAuthService>()
|
builder.RegisterModule<DataStoreModule>();
|
||||||
|
builder.RegisterModule<APIModule>();
|
||||||
.AddTransient(_ => Configuration.GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig())
|
|
||||||
.AddSingleton(svc => InitUtils.InitLogger(svc.GetRequiredService<CoreConfig>(), "api"))
|
|
||||||
|
|
||||||
.AddTransient<DbConnectionCountHolder>()
|
|
||||||
.AddTransient<DbConnectionFactory>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
@ -7,6 +7,9 @@ using System.Threading;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using App.Metrics;
|
using App.Metrics;
|
||||||
|
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Core;
|
||||||
|
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
@ -15,11 +18,16 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
using PluralKit.Bot.Commands;
|
using PluralKit.Bot.Commands;
|
||||||
using PluralKit.Bot.CommandSystem;
|
using PluralKit.Bot.CommandSystem;
|
||||||
|
using PluralKit.Core;
|
||||||
|
|
||||||
using Sentry;
|
using Sentry;
|
||||||
|
using Sentry.Infrastructure;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
|
using SystemClock = NodaTime.SystemClock;
|
||||||
|
|
||||||
namespace PluralKit.Bot
|
namespace PluralKit.Bot
|
||||||
{
|
{
|
||||||
class Initialize
|
class Initialize
|
||||||
@ -45,105 +53,57 @@ namespace PluralKit.Bot
|
|||||||
args.Cancel = true;
|
args.Cancel = true;
|
||||||
token.Cancel();
|
token.Cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var builder = new ContainerBuilder();
|
||||||
|
builder.RegisterInstance(_config);
|
||||||
|
builder.RegisterModule(new ConfigModule<BotConfig>("Bot"));
|
||||||
|
builder.RegisterModule(new LoggingModule("bot"));
|
||||||
|
builder.RegisterModule(new MetricsModule());
|
||||||
|
builder.RegisterModule<DataStoreModule>();
|
||||||
|
builder.RegisterModule<BotModule>();
|
||||||
|
|
||||||
|
using var services = builder.Build();
|
||||||
|
|
||||||
using (var services = BuildServiceProvider())
|
var logger = services.Resolve<ILogger>().ForContext<Initialize>();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
SchemaService.Initialize();
|
SchemaService.Initialize();
|
||||||
|
|
||||||
|
var coreConfig = services.Resolve<CoreConfig>();
|
||||||
|
var botConfig = services.Resolve<BotConfig>();
|
||||||
|
var schema = services.Resolve<SchemaService>();
|
||||||
|
|
||||||
|
using var _ = Sentry.SentrySdk.Init(coreConfig.SentryUrl);
|
||||||
|
|
||||||
var logger = services.GetRequiredService<ILogger>().ForContext<Initialize>();
|
logger.Information("Connecting to database");
|
||||||
var coreConfig = services.GetRequiredService<CoreConfig>();
|
await schema.ApplyMigrations();
|
||||||
var botConfig = services.GetRequiredService<BotConfig>();
|
|
||||||
var schema = services.GetRequiredService<SchemaService>();
|
|
||||||
|
|
||||||
using (Sentry.SentrySdk.Init(coreConfig.SentryUrl))
|
logger.Information("Connecting to Discord");
|
||||||
|
var client = services.Resolve<DiscordShardedClient>();
|
||||||
|
await client.LoginAsync(TokenType.Bot, botConfig.Token);
|
||||||
|
|
||||||
|
logger.Information("Initializing bot");
|
||||||
|
await client.StartAsync();
|
||||||
|
await services.Resolve<Bot>().Init();
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
logger.Information("Connecting to database");
|
await Task.Delay(-1, token.Token);
|
||||||
await schema.ApplyMigrations();
|
|
||||||
|
|
||||||
logger.Information("Connecting to Discord");
|
|
||||||
var client = services.GetRequiredService<IDiscordClient>() as DiscordShardedClient;
|
|
||||||
await client.LoginAsync(TokenType.Bot, botConfig.Token);
|
|
||||||
|
|
||||||
logger.Information("Initializing bot");
|
|
||||||
await services.GetRequiredService<Bot>().Init();
|
|
||||||
|
|
||||||
await client.StartAsync();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Delay(-1, token.Token);
|
|
||||||
}
|
|
||||||
catch (TaskCanceledException) { } // We'll just exit normally
|
|
||||||
logger.Information("Shutting down");
|
|
||||||
}
|
}
|
||||||
|
catch (TaskCanceledException) { } // We'll just exit normally
|
||||||
}
|
}
|
||||||
}
|
catch (Exception e)
|
||||||
|
|
||||||
public ServiceProvider BuildServiceProvider() => new ServiceCollection()
|
|
||||||
.AddTransient(_ => _config.GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig())
|
|
||||||
.AddTransient(_ => _config.GetSection("PluralKit").GetSection("Bot").Get<BotConfig>() ?? new BotConfig())
|
|
||||||
|
|
||||||
.AddSingleton<DbConnectionCountHolder>()
|
|
||||||
.AddTransient<DbConnectionFactory>()
|
|
||||||
.AddTransient<SchemaService>()
|
|
||||||
|
|
||||||
.AddSingleton<IDiscordClient, DiscordShardedClient>(_ => new DiscordShardedClient(new DiscordSocketConfig
|
|
||||||
{
|
{
|
||||||
MessageCacheSize = 0,
|
logger.Fatal(e, "Unrecoverable error while initializing bot");
|
||||||
ConnectionTimeout = 2*60*1000,
|
}
|
||||||
ExclusiveBulkDelete = true,
|
|
||||||
LargeThreshold = 50,
|
|
||||||
DefaultRetryMode = RetryMode.RetryTimeouts | RetryMode.RetryRatelimit
|
|
||||||
// Commented this out since Debug actually sends, uh, quite a lot that's not necessary in production
|
|
||||||
// but leaving it here in case I (or someone else) get[s] confused about why logging isn't working again :p
|
|
||||||
// LogLevel = LogSeverity.Debug // We filter log levels in Serilog, so just pass everything through (Debug is lower than Verbose)
|
|
||||||
}))
|
|
||||||
.AddSingleton<Bot>()
|
|
||||||
.AddSingleton(_ => new HttpClient { Timeout = TimeSpan.FromSeconds(5) })
|
|
||||||
.AddTransient<CommandTree>()
|
|
||||||
|
|
||||||
.AddTransient<SystemCommands>()
|
|
||||||
.AddTransient<MemberCommands>()
|
|
||||||
.AddTransient<SwitchCommands>()
|
|
||||||
.AddTransient<LinkCommands>()
|
|
||||||
.AddTransient<APICommands>()
|
|
||||||
.AddTransient<ImportExportCommands>()
|
|
||||||
.AddTransient<HelpCommands>()
|
|
||||||
.AddTransient<ModCommands>()
|
|
||||||
.AddTransient<MiscCommands>()
|
|
||||||
.AddTransient<AutoproxyCommands>()
|
|
||||||
|
|
||||||
.AddTransient<EmbedService>()
|
|
||||||
.AddTransient<ProxyService>()
|
|
||||||
.AddTransient<LogChannelService>()
|
|
||||||
.AddTransient<DataFileService>()
|
|
||||||
.AddTransient<WebhookExecutorService>()
|
|
||||||
|
|
||||||
.AddTransient<ProxyCacheService>()
|
logger.Information("Shutting down");
|
||||||
.AddSingleton<WebhookCacheService>()
|
}
|
||||||
.AddSingleton<AutoproxyCacheService>()
|
|
||||||
.AddSingleton<ShardInfoService>()
|
|
||||||
.AddSingleton<CpuStatService>()
|
|
||||||
|
|
||||||
.AddTransient<IDataStore, PostgresDataStore>()
|
|
||||||
|
|
||||||
.AddSingleton(svc => InitUtils.InitMetrics(svc.GetRequiredService<CoreConfig>()))
|
|
||||||
.AddSingleton<PeriodicStatCollector>()
|
|
||||||
|
|
||||||
.AddScoped(_ => new Sentry.Scope(null))
|
|
||||||
.AddTransient<PKEventHandler>()
|
|
||||||
|
|
||||||
.AddScoped<EventIdProvider>()
|
|
||||||
.AddSingleton(svc => new LoggerProvider(svc.GetRequiredService<CoreConfig>(), "bot"))
|
|
||||||
.AddScoped(svc => svc.GetRequiredService<LoggerProvider>().RootLogger.ForContext("EventId", svc.GetRequiredService<EventIdProvider>().EventId))
|
|
||||||
|
|
||||||
.AddMemoryCache()
|
|
||||||
|
|
||||||
.BuildServiceProvider();
|
|
||||||
}
|
}
|
||||||
class Bot
|
class Bot
|
||||||
{
|
{
|
||||||
private IServiceProvider _services;
|
private ILifetimeScope _services;
|
||||||
private DiscordShardedClient _client;
|
private DiscordShardedClient _client;
|
||||||
private Timer _updateTimer;
|
private Timer _updateTimer;
|
||||||
private IMetrics _metrics;
|
private IMetrics _metrics;
|
||||||
@ -151,7 +111,7 @@ namespace PluralKit.Bot
|
|||||||
private ILogger _logger;
|
private ILogger _logger;
|
||||||
private PKPerformanceEventListener _pl;
|
private PKPerformanceEventListener _pl;
|
||||||
|
|
||||||
public Bot(IServiceProvider services, IDiscordClient client, IMetrics metrics, PeriodicStatCollector collector, ILogger logger)
|
public Bot(ILifetimeScope services, IDiscordClient client, IMetrics metrics, PeriodicStatCollector collector, ILogger logger)
|
||||||
{
|
{
|
||||||
_pl = new PKPerformanceEventListener();
|
_pl = new PKPerformanceEventListener();
|
||||||
_services = services;
|
_services = services;
|
||||||
@ -167,12 +127,12 @@ namespace PluralKit.Bot
|
|||||||
_client.ShardReady += ShardReady;
|
_client.ShardReady += ShardReady;
|
||||||
_client.Log += FrameworkLog;
|
_client.Log += FrameworkLog;
|
||||||
|
|
||||||
_client.MessageReceived += (msg) => HandleEvent(s => s.AddMessageBreadcrumb(msg), eh => eh.HandleMessage(msg));
|
_client.MessageReceived += (msg) => HandleEvent(eh => eh.HandleMessage(msg));
|
||||||
_client.ReactionAdded += (msg, channel, reaction) => HandleEvent(s => s.AddReactionAddedBreadcrumb(msg, channel, reaction), eh => eh.HandleReactionAdded(msg, channel, reaction));
|
_client.ReactionAdded += (msg, channel, reaction) => HandleEvent(eh => eh.HandleReactionAdded(msg, channel, reaction));
|
||||||
_client.MessageDeleted += (msg, channel) => HandleEvent(s => s.AddMessageDeleteBreadcrumb(msg, channel), eh => eh.HandleMessageDeleted(msg, channel));
|
_client.MessageDeleted += (msg, channel) => HandleEvent(eh => eh.HandleMessageDeleted(msg, channel));
|
||||||
_client.MessagesBulkDeleted += (msgs, channel) => HandleEvent(s => s.AddMessageBulkDeleteBreadcrumb(msgs, channel), eh => eh.HandleMessagesBulkDelete(msgs, channel));
|
_client.MessagesBulkDeleted += (msgs, channel) => HandleEvent(eh => eh.HandleMessagesBulkDelete(msgs, channel));
|
||||||
|
|
||||||
_services.GetService<ShardInfoService>().Init(_client);
|
_services.Resolve<ShardInfoService>().Init(_client);
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@ -218,24 +178,24 @@ namespace PluralKit.Bot
|
|||||||
|
|
||||||
private Task ShardReady(DiscordSocketClient shardClient)
|
private Task ShardReady(DiscordSocketClient shardClient)
|
||||||
{
|
{
|
||||||
_logger.Information("Shard {Shard} connected", shardClient.ShardId);
|
_logger.Information("Shard {Shard} connected to {ChannelCount} channels in {GuildCount} guilds", shardClient.ShardId, shardClient.Guilds.Sum(g => g.Channels.Count), shardClient.Guilds.Count);
|
||||||
Console.WriteLine($"Shard #{shardClient.ShardId} connected to {shardClient.Guilds.Sum(g => g.Channels.Count)} channels in {shardClient.Guilds.Count} guilds.");
|
|
||||||
|
|
||||||
if (shardClient.ShardId == 0)
|
if (shardClient.ShardId == 0)
|
||||||
{
|
{
|
||||||
_updateTimer = new Timer((_) => {
|
_updateTimer = new Timer((_) => {
|
||||||
HandleEvent(s => s.AddPeriodicBreadcrumb(), __ => UpdatePeriodic());
|
HandleEvent(_ => UpdatePeriodic());
|
||||||
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
|
||||||
|
|
||||||
Console.WriteLine(
|
_logger.Information("PluralKit started as {Username}#{Discriminator} ({Id})", _client.CurrentUser.Username, _client.CurrentUser.Discriminator, _client.CurrentUser.Id);
|
||||||
$"PluralKit started as {_client.CurrentUser.Username}#{_client.CurrentUser.Discriminator} ({_client.CurrentUser.Id}).");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task HandleEvent(Action<Scope> breadcrumbFactory, Func<PKEventHandler, Task> handler)
|
private Task HandleEvent(Func<PKEventHandler, Task> handler)
|
||||||
{
|
{
|
||||||
|
_logger.Debug("Received event");
|
||||||
|
|
||||||
// Inner function so we can await the handler without stalling the entire pipeline
|
// Inner function so we can await the handler without stalling the entire pipeline
|
||||||
async Task Inner()
|
async Task Inner()
|
||||||
{
|
{
|
||||||
@ -243,46 +203,36 @@ namespace PluralKit.Bot
|
|||||||
// This prevents any synchronous nonsense from also stalling the pipeline before the first await point
|
// This prevents any synchronous nonsense from also stalling the pipeline before the first await point
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
|
|
||||||
// Create a DI scope for this event
|
using var containerScope = _services.BeginLifetimeScope();
|
||||||
// and log the breadcrumb to the newly created (in-svc-scope) Sentry scope
|
var sentryScope = containerScope.Resolve<Scope>();
|
||||||
using (var scope = _services.CreateScope())
|
var eventHandler = containerScope.Resolve<PKEventHandler>();
|
||||||
{
|
|
||||||
var evtid = scope.ServiceProvider.GetService<EventIdProvider>().EventId;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await handler(scope.ServiceProvider.GetRequiredService<PKEventHandler>());
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
var sentryScope = scope.ServiceProvider.GetRequiredService<Scope>();
|
|
||||||
sentryScope.SetTag("evtid", evtid.ToString());
|
|
||||||
breadcrumbFactory(sentryScope);
|
|
||||||
|
|
||||||
HandleRuntimeError(e, scope.ServiceProvider);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await handler(eventHandler);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await HandleRuntimeError(eventHandler, e, sentryScope);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable 4014
|
var _ = Inner();
|
||||||
Inner();
|
|
||||||
#pragma warning restore 4014
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleRuntimeError(Exception e, IServiceProvider services)
|
private async Task HandleRuntimeError(PKEventHandler eventHandler, Exception exc, Scope scope)
|
||||||
{
|
{
|
||||||
var logger = services.GetRequiredService<ILogger>();
|
_logger.Error(exc, "Exception in bot event handler");
|
||||||
var scope = services.GetRequiredService<Scope>();
|
|
||||||
|
|
||||||
logger.Error(e, "Exception in bot event handler");
|
var evt = new SentryEvent(exc);
|
||||||
|
|
||||||
var evt = new SentryEvent(e);
|
|
||||||
|
|
||||||
// Don't blow out our Sentry budget on sporadic not-our-problem erorrs
|
// Don't blow out our Sentry budget on sporadic not-our-problem erorrs
|
||||||
if (e.IsOurProblem())
|
if (exc.IsOurProblem())
|
||||||
SentrySdk.CaptureEvent(evt, scope);
|
SentrySdk.CaptureEvent(evt, scope);
|
||||||
|
|
||||||
|
// Once we've sent it to Sentry, report it to the user
|
||||||
|
await eventHandler.ReportError(evt, exc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,28 +242,33 @@ namespace PluralKit.Bot
|
|||||||
private IMetrics _metrics;
|
private IMetrics _metrics;
|
||||||
private DiscordShardedClient _client;
|
private DiscordShardedClient _client;
|
||||||
private DbConnectionFactory _connectionFactory;
|
private DbConnectionFactory _connectionFactory;
|
||||||
private IServiceProvider _services;
|
private ILifetimeScope _services;
|
||||||
private CommandTree _tree;
|
private CommandTree _tree;
|
||||||
private IDataStore _data;
|
private Scope _sentryScope;
|
||||||
|
|
||||||
public PKEventHandler(ProxyService proxy, ILogger logger, IMetrics metrics, IDiscordClient client, DbConnectionFactory connectionFactory, IServiceProvider services, CommandTree tree, IDataStore data)
|
// We're defining in the Autofac module that this class is instantiated with one instance per event
|
||||||
|
// This means that the HandleMessage function will either be called once, or not at all
|
||||||
|
// The ReportError function will be called on an error, and needs to refer back to the "trigger message"
|
||||||
|
// hence, we just store it in a local variable, ignoring it entirely if it's null.
|
||||||
|
private IUserMessage _msg = null;
|
||||||
|
|
||||||
|
public PKEventHandler(ProxyService proxy, ILogger logger, IMetrics metrics, DiscordShardedClient client, DbConnectionFactory connectionFactory, ILifetimeScope services, CommandTree tree, Scope sentryScope)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
_proxy = proxy;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_metrics = metrics;
|
_metrics = metrics;
|
||||||
_client = (DiscordShardedClient) client;
|
_client = client;
|
||||||
_connectionFactory = connectionFactory;
|
_connectionFactory = connectionFactory;
|
||||||
_services = services;
|
_services = services;
|
||||||
_tree = tree;
|
_tree = tree;
|
||||||
_data = data;
|
_sentryScope = sentryScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task HandleMessage(SocketMessage arg)
|
public async Task HandleMessage(SocketMessage arg)
|
||||||
{
|
{
|
||||||
if (_client.GetShardFor((arg.Channel as IGuildChannel)?.Guild).ConnectionState != ConnectionState.Connected)
|
if (_client.GetShardFor((arg.Channel as IGuildChannel)?.Guild).ConnectionState != ConnectionState.Connected)
|
||||||
return; // Discard messages while the bot "catches up" to avoid unnecessary CPU pressure causing timeouts
|
return; // Discard messages while the bot "catches up" to avoid unnecessary CPU pressure causing timeouts
|
||||||
|
|
||||||
|
|
||||||
RegisterMessageMetrics(arg);
|
RegisterMessageMetrics(arg);
|
||||||
|
|
||||||
// Ignore system messages (member joined, message pinned, etc)
|
// Ignore system messages (member joined, message pinned, etc)
|
||||||
@ -323,6 +278,16 @@ namespace PluralKit.Bot
|
|||||||
// Ignore bot messages
|
// Ignore bot messages
|
||||||
if (msg.Author.IsBot || msg.Author.IsWebhook) return;
|
if (msg.Author.IsBot || msg.Author.IsWebhook) return;
|
||||||
|
|
||||||
|
// Add message info as Sentry breadcrumb
|
||||||
|
_msg = msg;
|
||||||
|
_sentryScope.AddBreadcrumb(msg.Content, "event.message", data: new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{"user", msg.Author.Id.ToString()},
|
||||||
|
{"channel", msg.Channel.Id.ToString()},
|
||||||
|
{"guild", ((msg.Channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
||||||
|
{"message", msg.Id.ToString()},
|
||||||
|
});
|
||||||
|
|
||||||
int argPos = -1;
|
int argPos = -1;
|
||||||
// Check if message starts with the command prefix
|
// Check if message starts with the command prefix
|
||||||
if (msg.Content.StartsWith("pk;", StringComparison.InvariantCultureIgnoreCase)) argPos = 3;
|
if (msg.Content.StartsWith("pk;", StringComparison.InvariantCultureIgnoreCase)) argPos = 3;
|
||||||
@ -349,18 +314,8 @@ namespace PluralKit.Bot
|
|||||||
system = await conn.QueryFirstOrDefaultAsync<PKSystem>(
|
system = await conn.QueryFirstOrDefaultAsync<PKSystem>(
|
||||||
"select systems.* from systems, accounts where accounts.uid = @Id and systems.id = accounts.system",
|
"select systems.* from systems, accounts where accounts.uid = @Id and systems.id = accounts.system",
|
||||||
new {Id = msg.Author.Id});
|
new {Id = msg.Author.Id});
|
||||||
|
|
||||||
try
|
await _tree.ExecuteCommand(new Context(_services, msg, argPos, system));
|
||||||
{
|
|
||||||
await _tree.ExecuteCommand(new Context(_services, msg, argPos, system));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
await HandleCommandError(msg, e);
|
|
||||||
// HandleCommandError only *reports* the error, we gotta pass it through to the parent
|
|
||||||
// error handler by rethrowing:
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -376,16 +331,19 @@ namespace PluralKit.Bot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleCommandError(SocketUserMessage msg, Exception exception)
|
public async Task ReportError(SentryEvent evt, Exception exc)
|
||||||
{
|
{
|
||||||
|
// If we don't have a "trigger message", bail
|
||||||
|
if (_msg == null) return;
|
||||||
|
|
||||||
// This function *specifically* handles reporting a command execution error to the user.
|
// This function *specifically* handles reporting a command execution error to the user.
|
||||||
// We'll fetch the event ID and send a user-facing error message.
|
// We'll fetch the event ID and send a user-facing error message.
|
||||||
// ONLY IF this error's actually our problem. As for what defines an error as "our problem",
|
// ONLY IF this error's actually our problem. As for what defines an error as "our problem",
|
||||||
// check the extension method :)
|
// check the extension method :)
|
||||||
if (exception.IsOurProblem())
|
if (exc.IsOurProblem())
|
||||||
{
|
{
|
||||||
var eid = _services.GetService<EventIdProvider>().EventId;
|
var eid = evt.EventId;
|
||||||
await msg.Channel.SendMessageAsync(
|
await _msg.Channel.SendMessageAsync(
|
||||||
$"{Emojis.Error} Internal error occurred. Please join the support server (<https://discord.gg/PczBt78>), and send the developer this ID: `{eid}`\nBe sure to include a description of what you were doing to make the error occur.");
|
$"{Emojis.Error} Internal error occurred. Please join the support server (<https://discord.gg/PczBt78>), and send the developer this ID: `{eid}`\nBe sure to include a description of what you were doing to make the error occur.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,12 +359,43 @@ namespace PluralKit.Bot
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task HandleReactionAdded(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel,
|
public Task HandleReactionAdded(Cacheable<IUserMessage, ulong> message, ISocketMessageChannel channel,
|
||||||
SocketReaction reaction) => _proxy.HandleReactionAddedAsync(message, channel, reaction);
|
SocketReaction reaction)
|
||||||
|
{
|
||||||
|
_sentryScope.AddBreadcrumb("", "event.reaction", data: new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"user", reaction.UserId.ToString()},
|
||||||
|
{"channel", channel.Id.ToString()},
|
||||||
|
{"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
||||||
|
{"message", message.Id.ToString()},
|
||||||
|
{"reaction", reaction.Emote.Name}
|
||||||
|
});
|
||||||
|
|
||||||
|
return _proxy.HandleReactionAddedAsync(message, channel, reaction);
|
||||||
|
}
|
||||||
|
|
||||||
public Task HandleMessageDeleted(Cacheable<IMessage, ulong> message, ISocketMessageChannel channel) =>
|
public Task HandleMessageDeleted(Cacheable<IMessage, ulong> message, ISocketMessageChannel channel)
|
||||||
_proxy.HandleMessageDeletedAsync(message, channel);
|
{
|
||||||
|
_sentryScope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"channel", channel.Id.ToString()},
|
||||||
|
{"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
||||||
|
{"message", message.Id.ToString()},
|
||||||
|
});
|
||||||
|
|
||||||
|
return _proxy.HandleMessageDeletedAsync(message, channel);
|
||||||
|
}
|
||||||
|
|
||||||
public Task HandleMessagesBulkDelete(IReadOnlyCollection<Cacheable<IMessage, ulong>> messages,
|
public Task HandleMessagesBulkDelete(IReadOnlyCollection<Cacheable<IMessage, ulong>> messages,
|
||||||
IMessageChannel channel) => _proxy.HandleMessageBulkDeleteAsync(messages, channel);
|
IMessageChannel channel)
|
||||||
|
{
|
||||||
|
_sentryScope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"channel", channel.Id.ToString()},
|
||||||
|
{"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
||||||
|
{"messages", string.Join(",", messages.Select(m => m.Id))},
|
||||||
|
});
|
||||||
|
|
||||||
|
return _proxy.HandleMessageBulkDeleteAsync(messages, channel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Discord;
|
|
||||||
using Discord.WebSocket;
|
|
||||||
using Sentry;
|
|
||||||
|
|
||||||
namespace PluralKit.Bot
|
|
||||||
{
|
|
||||||
public static class BreadcrumbExtensions
|
|
||||||
{
|
|
||||||
public static void AddMessageBreadcrumb(this Scope scope, SocketMessage msg)
|
|
||||||
{
|
|
||||||
scope.AddBreadcrumb(msg.Content, "event.message", data: new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{"user", msg.Author.Id.ToString()},
|
|
||||||
{"channel", msg.Channel.Id.ToString()},
|
|
||||||
{"guild", ((msg.Channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
|
||||||
{"message", msg.Id.ToString()},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddReactionAddedBreadcrumb(this Scope scope, Cacheable<IUserMessage, ulong> message,
|
|
||||||
ISocketMessageChannel channel, SocketReaction reaction)
|
|
||||||
{
|
|
||||||
scope.AddBreadcrumb("", "event.reaction", data: new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{"user", reaction.UserId.ToString()},
|
|
||||||
{"channel", channel.Id.ToString()},
|
|
||||||
{"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
|
||||||
{"message", message.Id.ToString()},
|
|
||||||
{"reaction", reaction.Emote.Name}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddMessageDeleteBreadcrumb(this Scope scope, Cacheable<IMessage, ulong> message,
|
|
||||||
ISocketMessageChannel channel)
|
|
||||||
{
|
|
||||||
scope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{"channel", channel.Id.ToString()},
|
|
||||||
{"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
|
||||||
{"message", message.Id.ToString()},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddMessageBulkDeleteBreadcrumb(this Scope scope, IReadOnlyCollection<Cacheable<IMessage, ulong>> messages,
|
|
||||||
ISocketMessageChannel channel)
|
|
||||||
{
|
|
||||||
scope.AddBreadcrumb("", "event.messageDelete", data: new Dictionary<string, string>()
|
|
||||||
{
|
|
||||||
{"channel", channel.Id.ToString()},
|
|
||||||
{"guild", ((channel as IGuildChannel)?.GuildId ?? 0).ToString()},
|
|
||||||
{"messages", string.Join(",", messages.Select(m => m.Id))},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddPeriodicBreadcrumb(this Scope scope) => scope.AddBreadcrumb("", "periodic");
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,9 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
using App.Metrics;
|
using App.Metrics;
|
||||||
|
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Core;
|
||||||
|
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
|
||||||
@ -12,7 +15,7 @@ namespace PluralKit.Bot.CommandSystem
|
|||||||
{
|
{
|
||||||
public class Context
|
public class Context
|
||||||
{
|
{
|
||||||
private IServiceProvider _provider;
|
private ILifetimeScope _provider;
|
||||||
|
|
||||||
private readonly DiscordShardedClient _client;
|
private readonly DiscordShardedClient _client;
|
||||||
private readonly SocketUserMessage _message;
|
private readonly SocketUserMessage _message;
|
||||||
@ -24,14 +27,14 @@ namespace PluralKit.Bot.CommandSystem
|
|||||||
|
|
||||||
private Command _currentCommand;
|
private Command _currentCommand;
|
||||||
|
|
||||||
public Context(IServiceProvider provider, SocketUserMessage message, int commandParseOffset,
|
public Context(ILifetimeScope provider, SocketUserMessage message, int commandParseOffset,
|
||||||
PKSystem senderSystem)
|
PKSystem senderSystem)
|
||||||
{
|
{
|
||||||
_client = provider.GetRequiredService<IDiscordClient>() as DiscordShardedClient;
|
_client = provider.Resolve<DiscordShardedClient>();
|
||||||
_message = message;
|
_message = message;
|
||||||
_data = provider.GetRequiredService<IDataStore>();
|
_data = provider.Resolve<IDataStore>();
|
||||||
_senderSystem = senderSystem;
|
_senderSystem = senderSystem;
|
||||||
_metrics = provider.GetRequiredService<IMetrics>();
|
_metrics = provider.Resolve<IMetrics>();
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
_parameters = new Parameters(message.Content.Substring(commandParseOffset));
|
_parameters = new Parameters(message.Content.Substring(commandParseOffset));
|
||||||
}
|
}
|
||||||
@ -86,7 +89,7 @@ namespace PluralKit.Bot.CommandSystem
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await handler(_provider.GetRequiredService<T>());
|
await handler(_provider.Resolve<T>());
|
||||||
_metrics.Measure.Meter.Mark(BotMetrics.CommandsRun);
|
_metrics.Measure.Meter.Mark(BotMetrics.CommandsRun);
|
||||||
}
|
}
|
||||||
catch (PKSyntaxError e)
|
catch (PKSyntaxError e)
|
||||||
|
80
PluralKit.Bot/Modules.cs
Normal file
80
PluralKit.Bot/Modules.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using Discord;
|
||||||
|
using Discord.Rest;
|
||||||
|
using Discord.WebSocket;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
using PluralKit.Bot.Commands;
|
||||||
|
|
||||||
|
using Sentry;
|
||||||
|
|
||||||
|
namespace PluralKit.Bot
|
||||||
|
{
|
||||||
|
public class BotModule: Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
// Client
|
||||||
|
builder.Register(c => new DiscordShardedClient(new DiscordSocketConfig()
|
||||||
|
{
|
||||||
|
MessageCacheSize = 0,
|
||||||
|
ConnectionTimeout = 2 * 60 * 1000,
|
||||||
|
ExclusiveBulkDelete = true,
|
||||||
|
LargeThreshold = 50,
|
||||||
|
DefaultRetryMode = RetryMode.RetryTimeouts | RetryMode.RetryRatelimit
|
||||||
|
// Commented this out since Debug actually sends, uh, quite a lot that's not necessary in production
|
||||||
|
// but leaving it here in case I (or someone else) get[s] confused about why logging isn't working again :p
|
||||||
|
// LogLevel = LogSeverity.Debug // We filter log levels in Serilog, so just pass everything through (Debug is lower than Verbose)
|
||||||
|
})).AsSelf().As<BaseDiscordClient>().As<BaseSocketClient>().As<IDiscordClient>().SingleInstance();
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
builder.RegisterType<CommandTree>().AsSelf();
|
||||||
|
builder.RegisterType<SystemCommands>().AsSelf();
|
||||||
|
builder.RegisterType<MemberCommands>().AsSelf();
|
||||||
|
builder.RegisterType<SwitchCommands>().AsSelf();
|
||||||
|
builder.RegisterType<LinkCommands>().AsSelf();
|
||||||
|
builder.RegisterType<APICommands>().AsSelf();
|
||||||
|
builder.RegisterType<ImportExportCommands>().AsSelf();
|
||||||
|
builder.RegisterType<HelpCommands>().AsSelf();
|
||||||
|
builder.RegisterType<ModCommands>().AsSelf();
|
||||||
|
builder.RegisterType<MiscCommands>().AsSelf();
|
||||||
|
builder.RegisterType<AutoproxyCommands>().AsSelf();
|
||||||
|
|
||||||
|
// Bot core
|
||||||
|
builder.RegisterType<Bot>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<PKEventHandler>().AsSelf();
|
||||||
|
|
||||||
|
// Bot services
|
||||||
|
builder.RegisterType<EmbedService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<ProxyService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<LogChannelService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<DataFileService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<WebhookExecutorService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<ProxyCacheService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<WebhookCacheService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<AutoproxyCacheService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<ShardInfoService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<CpuStatService>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<PeriodicStatCollector>().AsSelf().SingleInstance();
|
||||||
|
|
||||||
|
// Sentry stuff
|
||||||
|
builder.Register(_ => new Scope(null)).AsSelf().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
// .NET stuff
|
||||||
|
builder.Populate(new ServiceCollection()
|
||||||
|
.AddMemoryCache());
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
builder.Register(c => new HttpClient
|
||||||
|
{
|
||||||
|
Timeout = TimeSpan.FromSeconds(5)
|
||||||
|
}).AsSelf().SingleInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
109
PluralKit.Core/Modules.cs
Normal file
109
PluralKit.Core/Modules.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using App.Metrics;
|
||||||
|
|
||||||
|
using Autofac;
|
||||||
|
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using Serilog.Formatting.Compact;
|
||||||
|
using Serilog.Sinks.SystemConsole.Themes;
|
||||||
|
|
||||||
|
namespace PluralKit.Core
|
||||||
|
{
|
||||||
|
public class DataStoreModule: Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
builder.RegisterType<DbConnectionCountHolder>().SingleInstance();
|
||||||
|
builder.RegisterType<DbConnectionFactory>().AsSelf().SingleInstance();
|
||||||
|
builder.RegisterType<PostgresDataStore>().AsSelf().As<IDataStore>();
|
||||||
|
builder.RegisterType<SchemaService>().AsSelf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConfigModule<T>: Module where T: new()
|
||||||
|
{
|
||||||
|
private string _submodule;
|
||||||
|
|
||||||
|
public ConfigModule(string submodule = null)
|
||||||
|
{
|
||||||
|
_submodule = submodule;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
// We're assuming IConfiguration is already available somehow - it comes from various places (auto-injected in ASP, etc)
|
||||||
|
|
||||||
|
// Register the CoreConfig and where to find it
|
||||||
|
builder.Register(c => c.Resolve<IConfiguration>().GetSection("PluralKit").Get<CoreConfig>() ?? new CoreConfig()).SingleInstance();
|
||||||
|
|
||||||
|
// Register the submodule config (BotConfig, etc) if specified
|
||||||
|
if (_submodule != null)
|
||||||
|
builder.Register(c => c.Resolve<IConfiguration>().GetSection("PluralKit").GetSection(_submodule).Get<T>() ?? new T()).SingleInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MetricsModule: Module
|
||||||
|
{
|
||||||
|
private readonly string _onlyContext;
|
||||||
|
|
||||||
|
public MetricsModule(string onlyContext = null)
|
||||||
|
{
|
||||||
|
_onlyContext = onlyContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Register(c => InitMetrics(c.Resolve<CoreConfig>()))
|
||||||
|
.AsSelf().As<IMetrics>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IMetricsRoot InitMetrics(CoreConfig config)
|
||||||
|
{
|
||||||
|
var builder = AppMetrics.CreateDefaultBuilder();
|
||||||
|
if (config.InfluxUrl != null && config.InfluxDb != null)
|
||||||
|
builder.Report.ToInfluxDb(config.InfluxUrl, config.InfluxDb);
|
||||||
|
if (_onlyContext != null)
|
||||||
|
builder.Filter.ByIncludingOnlyContext(_onlyContext);
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LoggingModule: Module
|
||||||
|
{
|
||||||
|
private readonly string _component;
|
||||||
|
|
||||||
|
public LoggingModule(string component)
|
||||||
|
{
|
||||||
|
_component = component;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Register(c => InitLogger(c.Resolve<CoreConfig>())).AsSelf().SingleInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ILogger InitLogger(CoreConfig config)
|
||||||
|
{
|
||||||
|
return new LoggerConfiguration()
|
||||||
|
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
|
||||||
|
.MinimumLevel.Debug()
|
||||||
|
.WriteTo.Async(a =>
|
||||||
|
a.File(
|
||||||
|
new RenderedCompactJsonFormatter(),
|
||||||
|
(config.LogDir ?? "logs") + $"/pluralkit.{_component}.log",
|
||||||
|
rollingInterval: RollingInterval.Day,
|
||||||
|
flushToDiskInterval: TimeSpan.FromSeconds(10),
|
||||||
|
restrictedToMinimumLevel: LogEventLevel.Information,
|
||||||
|
buffered: true))
|
||||||
|
.WriteTo.Async(a =>
|
||||||
|
a.Console(theme: AnsiConsoleTheme.Code, outputTemplate:"[{Timestamp:HH:mm:ss}] {Level:u3} {Message:lj}{NewLine}{Exception}"))
|
||||||
|
.CreateLogger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,8 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="App.Metrics" Version="3.2.0" />
|
<PackageReference Include="App.Metrics" Version="3.2.0" />
|
||||||
<PackageReference Include="App.Metrics.Reporting.InfluxDB" Version="3.2.0" />
|
<PackageReference Include="App.Metrics.Reporting.InfluxDB" Version="3.2.0" />
|
||||||
|
<PackageReference Include="Autofac" Version="4.9.4" />
|
||||||
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="5.0.1" />
|
||||||
<PackageReference Include="Dapper" Version="1.60.6" />
|
<PackageReference Include="Dapper" Version="1.60.6" />
|
||||||
<PackageReference Include="Dapper.Contrib" Version="1.60.1" />
|
<PackageReference Include="Dapper.Contrib" Version="1.60.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.0" />
|
||||||
|
@ -324,35 +324,7 @@ namespace PluralKit
|
|||||||
// Add global type mapper for ProxyTag compound type in Postgres
|
// Add global type mapper for ProxyTag compound type in Postgres
|
||||||
NpgsqlConnection.GlobalTypeMapper.MapComposite<ProxyTag>("proxy_tag");
|
NpgsqlConnection.GlobalTypeMapper.MapComposite<ProxyTag>("proxy_tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ILogger InitLogger(CoreConfig config, string component)
|
|
||||||
{
|
|
||||||
return new LoggerConfiguration()
|
|
||||||
.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)
|
|
||||||
.MinimumLevel.Debug()
|
|
||||||
.WriteTo.Async(a =>
|
|
||||||
a.File(
|
|
||||||
new RenderedCompactJsonFormatter(),
|
|
||||||
(config.LogDir ?? "logs") + $"/pluralkit.{component}.log",
|
|
||||||
rollingInterval: RollingInterval.Day,
|
|
||||||
flushToDiskInterval: TimeSpan.FromSeconds(10),
|
|
||||||
restrictedToMinimumLevel: LogEventLevel.Information,
|
|
||||||
buffered: true))
|
|
||||||
.WriteTo.Async(a =>
|
|
||||||
a.Console(theme: AnsiConsoleTheme.Code, outputTemplate:"[{Timestamp:HH:mm:ss}] [{EventId}] {Level:u3} {Message:lj}{NewLine}{Exception}"))
|
|
||||||
.CreateLogger();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IMetrics InitMetrics(CoreConfig config, string onlyContext = null)
|
|
||||||
{
|
|
||||||
var builder = AppMetrics.CreateDefaultBuilder();
|
|
||||||
if (config.InfluxUrl != null && config.InfluxDb != null)
|
|
||||||
builder.Report.ToInfluxDb(config.InfluxUrl, config.InfluxDb);
|
|
||||||
if (onlyContext != null)
|
|
||||||
builder.Filter.ByIncludingOnlyContext(onlyContext);
|
|
||||||
return builder.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static JsonSerializerSettings BuildSerializerSettings() => new JsonSerializerSettings().BuildSerializerSettings();
|
public static JsonSerializerSettings BuildSerializerSettings() => new JsonSerializerSettings().BuildSerializerSettings();
|
||||||
|
|
||||||
public static JsonSerializerSettings BuildSerializerSettings(this JsonSerializerSettings settings)
|
public static JsonSerializerSettings BuildSerializerSettings(this JsonSerializerSettings settings)
|
||||||
@ -362,18 +334,6 @@ namespace PluralKit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LoggerProvider
|
|
||||||
{
|
|
||||||
private CoreConfig _config;
|
|
||||||
public ILogger RootLogger { get; }
|
|
||||||
|
|
||||||
public LoggerProvider(CoreConfig config, string component)
|
|
||||||
{
|
|
||||||
_config = config;
|
|
||||||
RootLogger = InitUtils.InitLogger(_config, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
public class UlongEncodeAsLongHandler : SqlMapper.TypeHandler<ulong>
|
||||||
{
|
{
|
||||||
public override ulong Parse(object value)
|
public override ulong Parse(object value)
|
||||||
@ -660,16 +620,6 @@ namespace PluralKit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class EventIdProvider
|
|
||||||
{
|
|
||||||
public Guid EventId { get; }
|
|
||||||
|
|
||||||
public EventIdProvider()
|
|
||||||
{
|
|
||||||
EventId = Guid.NewGuid();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ConnectionUtils
|
public static class ConnectionUtils
|
||||||
{
|
{
|
||||||
public static async IAsyncEnumerable<T> QueryStreamAsync<T>(this DbConnectionFactory connFactory, string sql, object param)
|
public static async IAsyncEnumerable<T> QueryStreamAsync<T>(this DbConnectionFactory connFactory, string sql, object param)
|
||||||
|
Loading…
Reference in New Issue
Block a user