Created
November 3, 2020 14:09
-
-
Save cking/307603e320e0e5df5645fa90257cd3fe to your computer and use it in GitHub Desktop.
D#+ with Qmmands and GenericHost
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// <copyright file="Program.cs" company="z0ne"> | |
// Copyright (c) z0ne. All rights reserved. | |
// </copyright> | |
namespace Maria | |
{ | |
using System; | |
using System.Linq; | |
using CommandLine; | |
using dotenv.net; | |
using Microsoft.Extensions.Hosting; | |
using Serilog; | |
using Serilog.Core; | |
using Serilog.Events; | |
using Serilog.Exceptions; | |
using Serilog.Sinks.SystemConsole.Themes; | |
/// <summary> | |
/// Program entry. | |
/// </summary> | |
internal static class Program | |
{ | |
private static LoggingLevelSwitch ConfigureLogger() | |
{ | |
// ReSharper disable once UseObjectOrCollectionInitializer | |
var levelSwitch = new LoggingLevelSwitch(); | |
#if DEBUG | |
levelSwitch.MinimumLevel = LogEventLevel.Verbose; | |
#endif | |
var lc = new LoggerConfiguration().MinimumLevel.ControlledBy(levelSwitch) | |
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning) | |
.MinimumLevel.Override("System", LogEventLevel.Warning) | |
.MinimumLevel.Override("DSharpPlus", LogEventLevel.Information) | |
.Enrich.WithExceptionDetails() | |
.Enrich.FromLogContext() | |
.WriteTo.Console( | |
theme: AnsiConsoleTheme.Literate, | |
outputTemplate: | |
"[{Level:u1} {Timestamp:HH:mm:ss}] {SourceContext}: {Message:lj}{Properties:j}{NewLine}{Exception}"); | |
Log.Logger = lc.CreateLogger(); | |
return levelSwitch; | |
} | |
public static IHostBuilder CreateHostBuilder(string[] args) | |
{ | |
var startup = new Startup(); | |
return startup.Initialize(); | |
} | |
private static int Main(string[] args) | |
{ | |
// support .env files | |
DotEnv.AutoConfig(); | |
var levelSwitch = ConfigureLogger(); | |
using var parser = Parser.Default; | |
return parser.ParseArguments<Args>(args) | |
.MapResult( | |
options => new Startup(options, levelSwitch).Run(), | |
errors => | |
{ | |
if (errors.Any(e => e is HelpRequestedError || e is VersionRequestedError)) | |
{ | |
return -1; | |
} | |
Console.WriteLine("Failed to start."); | |
return -2; | |
}); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// <copyright file="Startup.cs" company="z0ne"> | |
// Copyright (c) z0ne. All rights reserved. | |
// </copyright> | |
namespace Maria | |
{ | |
using System; | |
using System.IO; | |
using Autofac; | |
using Autofac.Extensions.DependencyInjection; | |
using DSharpPlus; | |
using Maria.Configuration; | |
using Maria.Data; | |
using Maria.Naruse; | |
using MediatR; | |
using Microsoft.EntityFrameworkCore; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Hosting; | |
using Microsoft.Extensions.Options; | |
using Qmmands; | |
using Serilog; | |
using Serilog.Core; | |
using Serilog.Events; | |
using Serilog.Extensions.Logging; | |
using StackExchange.Redis; | |
internal class Startup | |
{ | |
private readonly Args args; | |
private readonly LoggingLevelSwitch loggingLevelSwitch; | |
public Startup(Args args, LoggingLevelSwitch loggingLevelSwitch) | |
{ | |
this.args = args; | |
this.loggingLevelSwitch = loggingLevelSwitch; | |
} | |
public Startup() | |
{ | |
this.args = new Args(); | |
this.loggingLevelSwitch = new LoggingLevelSwitch(); | |
} | |
public IContainer Container { get; private set; } | |
public IServiceProvider ServiceProvider { get; private set; } | |
public void ConfigureAppConfiguration(HostBuilderContext context, IConfigurationBuilder configuration) | |
{ | |
var env = context.HostingEnvironment; | |
configuration.SetBasePath(Directory.GetCurrentDirectory()); | |
var configFile = args.ConfigFile; | |
if (string.IsNullOrEmpty(configFile)) | |
{ | |
configFile = "maria.yml"; | |
} | |
configuration.AddYamlFile(configFile, false, true) | |
.AddYamlFile(GetEnvPath(configFile, env.EnvironmentName), true, true); | |
if (env.IsDevelopment()) | |
{ | |
configuration.AddUserSecrets(typeof(Program).Assembly, true, false); | |
} | |
configuration.AddEnvironmentVariables("MARIA_"); | |
configuration.AddInMemoryCollection(args.ToInMemoryCollection()); | |
} | |
public void ConfigureContainer(ContainerBuilder builder) | |
{ | |
} | |
public void ConfigureServices(HostBuilderContext context, IServiceCollection services) | |
{ | |
// logging | |
services.AddLogging(); | |
// config | |
services.AddOptions<RootOptions>().Bind(context.Configuration).ValidateDataAnnotations(); | |
// database | |
services.AddDbContext<ApplicationContext>( | |
b => b.UseNpgsql( | |
context.Configuration.GetConnectionString("Postgres"), | |
optionsBuilder => optionsBuilder.EnableRetryOnFailure(5))); | |
services.AddStackExchangeRedisCache( | |
o => o.Configuration = context.Configuration.GetConnectionString("Redis")); | |
// discord client | |
services.AddSingleton( | |
provider => | |
{ | |
var opts = provider.GetService<IOptions<RootOptions>>().Value; | |
return new DiscordShardedClient( | |
new DiscordConfiguration | |
{ | |
AutoReconnect = true, | |
GatewayCompressionLevel = GatewayCompressionLevel.Stream, | |
Intents = DiscordIntents.AllUnprivileged, | |
Token = opts.Discord.Token, | |
LoggerFactory = new SerilogLoggerFactory(), | |
}); | |
}); | |
services.AddSingleton( | |
provider => new CommandService( | |
new CommandServiceConfiguration | |
{ | |
StringComparison = StringComparison.InvariantCultureIgnoreCase, | |
DefaultRunMode = RunMode.Parallel, | |
})); | |
// TODO: extract to lib? | |
services.AddMediatR(typeof(Application)); | |
// TODO: extract to lib | |
// TODO: move to ConfigureContainer? | |
//services.AddSingleton<IJukeboxManager, JukeboxManager>(); | |
services.AddTransient<IHostedService, Application>(); | |
} | |
public IHostBuilder Initialize() | |
{ | |
if (args.Verbose) | |
{ | |
if (loggingLevelSwitch.MinimumLevel != LogEventLevel.Verbose) | |
{ | |
loggingLevelSwitch.MinimumLevel = LogEventLevel.Debug; | |
} | |
} | |
var hostBuilder = new HostBuilder() | |
.UseContentRoot(Directory.GetCurrentDirectory()) | |
.ConfigureHostConfiguration(config => config.AddEnvironmentVariables(prefix: "DOTNET_")) | |
.ConfigureAppConfiguration(ConfigureAppConfiguration) | |
.UseSerilog(dispose: true) | |
.UseServiceProviderFactory(new AutofacServiceProviderFactory()) | |
.ConfigureServices(ConfigureServices) | |
.ConfigureContainer<ContainerBuilder>(ConfigureContainer) | |
.UseConsoleLifetime(); | |
return hostBuilder; | |
} | |
public int Run() | |
{ | |
var host = Initialize().Build(); | |
host.Run(); | |
return 0; | |
} | |
private static string GetEnvPath(string path, string env) | |
{ | |
var dir = Path.GetDirectoryName(path) ?? "."; | |
var file = Path.GetFileNameWithoutExtension(path); | |
var ext = Path.GetExtension(path); | |
// ReSharper disable once InvertIf | |
if (string.IsNullOrEmpty(file)) | |
{ | |
file = ext; | |
ext = string.Empty; | |
} | |
return Path.Combine(dir, $"{file}.{env}{ext}"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment