Skip to content

Commit

Permalink
Rework Configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
HEJOK254 committed Sep 21, 2024
1 parent 7d5bf58 commit 3c54763
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 46 deletions.
4 changes: 1 addition & 3 deletions Commands/InteractionServiceHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using Discord.WebSocket;
using Serilog;

using QuickEdit.Config;

namespace QuickEdit.Commands;
internal sealed class InteractionServiceHandler
{
Expand Down Expand Up @@ -36,7 +34,7 @@ public async Task InitAsync()

try
{
_interactionService = new InteractionService(_client!.Rest, _interactionServiceConfig);
_interactionService = new InteractionService(_client.Rest, _interactionServiceConfig);
await RegisterModulesAsync();

// Can't simply get the result of the ExecuteCommandAsync, because of RunMode.Async
Expand Down
57 changes: 55 additions & 2 deletions Config/Config.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,65 @@
using Discord;
using Newtonsoft.Json;
using Serilog;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using System.ComponentModel.DataAnnotations;

namespace QuickEdit.Config;

internal sealed class ConfigManager
{
/// <summary>
/// Set up configuration services
/// </summary>
/// <param name="builder">The HostApplicationBuilder used for the program</param>
/// <returns>True is success, False if failure</returns>
internal static bool LoadConfiguration(HostApplicationBuilder builder)
{
try
{
// Binding
var discordConfig = builder.Configuration.GetRequiredSection(DiscordConfig.ConfigurationSectionName)
.Get<DiscordConfig>()!;

// Validation
ValidateConfig(discordConfig);

// Service registration (DI)
builder.Services.AddSingleton(discordConfig);
return true;
}
catch (ValidationException e)
{
Log.Fatal("Config parse error: {e}", e.Message);
Environment.ExitCode = 1;
return false;
}
catch (Exception e)
{
Log.Fatal("Failed to get config or create config service: {e}", e);
Environment.ExitCode = 1;
return false;
}
}

private static void ValidateConfig(object config)
{
var validationContext = new ValidationContext(config);
Validator.ValidateObject(config, validationContext, validateAllProperties: true);
}
}

public sealed class DiscordConfig
{
public string? Token { get; set; }
public const string ConfigurationSectionName = "DiscordConfig";

// TODO: Move the Token to user secrets at some point

[Required]
[RegularExpression(@"^([MN][\w-]{23,25})\.([\w-]{6})\.([\w-]{27,39})$", ErrorMessage = "Invalid token format")]
public required string Token { get; set; }
public ActivityType StatusType { get; set; }
public string? Status { get; set; }
public bool Debug { get; set; }
Expand Down
1 change: 1 addition & 0 deletions Discord QuickEdit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<PackageReference Include="Discord.Net" Version="3.15.3" />
<PackageReference Include="FFMpegCore" Version="5.1.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
<PackageReference Include="Serilog" Version="4.0.1" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
Expand Down
24 changes: 22 additions & 2 deletions Logger/SerilogConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
using System.Diagnostics;
using QuickEdit.Config;
using Serilog;
using Serilog.Core;

namespace QuickEdit.Logger;

public class SerilogConfiguration
public class SerilogConfiguration(DiscordConfig discordConfig)
{
// Use Debug by default, as it should get overwritten after the config is parsed and can help with Config issues
public static LoggingLevelSwitch LoggingLevel { get; set; } = new LoggingLevelSwitch(Serilog.Events.LogEventLevel.Debug);
private readonly DiscordConfig _discordConfig = discordConfig;

public static void ConfigureLogger()
{
var logDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs");
Expand All @@ -23,4 +25,22 @@ public static void ConfigureLogger()

Log.Logger = loggerConfig.CreateLogger();
}

internal bool SetLoggingLevelFromConfig()
{
try
{
LoggingLevel.MinimumLevel =
_discordConfig.Debug
? Serilog.Events.LogEventLevel.Debug
: Serilog.Events.LogEventLevel.Information;
return true;
}
catch (Exception e)
{
Log.Fatal("Failed to set minimum log level: {e}", e);
Environment.ExitCode = 1;
return false;
}
}
}
92 changes: 53 additions & 39 deletions Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using FFMpegCore.Helpers;
using QuickEdit.Commands;
using QuickEdit.Logger;
using QuickEdit.Config;
using Serilog;

using Serilog.Extensions.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -17,8 +17,6 @@ namespace QuickEdit;

internal class Program
{
private static readonly Config.DiscordConfig? discordConfig = new();

public static Task Main(string[] args) => new Program().MainAsync(args);

public async Task MainAsync(string[] args)
Expand All @@ -35,57 +33,73 @@ public async Task MainAsync(string[] args)
ContentRootPath = AppDomain.CurrentDomain.BaseDirectory
};

hostSettings.Configuration.AddJsonFile("config.json");
hostSettings.Configuration.AddCommandLine(args);

HostApplicationBuilder hostBuilder = Host.CreateApplicationBuilder(hostSettings);
ConfigureServices(hostBuilder.Services);
hostBuilder.Configuration.GetRequiredSection(nameof(DiscordConfig))
.Bind(discordConfig);

using var host = hostBuilder.Build();

// Change log level after getting Config
try
{
SerilogConfiguration.LoggingLevel.MinimumLevel =
discordConfig!.Debug
? Serilog.Events.LogEventLevel.Debug
: Serilog.Events.LogEventLevel.Information;
hostSettings.Configuration.AddJsonFile("config.json");
hostSettings.Configuration.AddCommandLine(args);
}
catch (FileNotFoundException)
{
// Can't log the file name using FileNotFoundException.FileName as it's just null
Log.Fatal("Couldn't find file 'config.json' in path: {path}", AppDomain.CurrentDomain.BaseDirectory);
return;
}
catch (Exception e)
{
Log.Fatal("Failed to set debugging level:\n{e}", e);
Environment.ExitCode = 1; // Exit without triggering DeepSource lol
Log.Fatal("Failed to add config providers:{e}", e);
return;
}

HostApplicationBuilder hostBuilder = Host.CreateApplicationBuilder(hostSettings);
if (!ConfigManager.LoadConfiguration(hostBuilder)) return;
if (!ConfigureServices(hostBuilder.Services)) return;

using var host = hostBuilder.Build();

// Change log level after getting Config
host.Services.GetRequiredService<SerilogConfiguration>().SetLoggingLevelFromConfig();

if (!CheckFFMpegExists()) return;

await host.RunAsync();
}

private static void ConfigureServices(IServiceCollection services)
/// <summary>
/// Configures Dependency Injection Services
/// </summary>
/// <param name="services">The service collection from the builder</param>
/// <returns>True is success, False if failure</returns>
private static bool ConfigureServices(IServiceCollection services)
{
var socketConfig = new DiscordSocketConfig()
try
{
GatewayIntents = GatewayIntents.None
};

var interactionServiceConfig = new InteractionServiceConfig()
var socketConfig = new DiscordSocketConfig()
{
GatewayIntents = GatewayIntents.None
};

var interactionServiceConfig = new InteractionServiceConfig()
{
UseCompiledLambda = true,
DefaultRunMode = RunMode.Async
};

services.AddSerilog();
services.AddTransient<SerilogConfiguration>();
services.AddSingleton(socketConfig);
services.AddSingleton(interactionServiceConfig);
services.AddSingleton<DiscordSocketClient>();
services.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordSocketClient>(), interactionServiceConfig));
services.AddSingleton<InteractionServiceHandler>();
services.AddHostedService<Bot>();
return true;
}
catch (Exception e)
{
UseCompiledLambda = true,
DefaultRunMode = RunMode.Async
};

services.AddSerilog();
services.AddSingleton(discordConfig!);
services.AddSingleton(socketConfig);
services.AddSingleton(interactionServiceConfig);
services.AddSingleton<DiscordSocketClient>();
services.AddSingleton(x => new InteractionService(x.GetRequiredService<DiscordSocketClient>(), interactionServiceConfig));
services.AddSingleton<InteractionServiceHandler>();
services.AddHostedService<Bot>();
Log.Fatal("Failed to configure services: {e}", e);
Environment.ExitCode = 1;
return false;
}
}

private static bool CheckFFMpegExists()
Expand All @@ -106,7 +120,7 @@ private static bool CheckFFMpegExists()
{
// It seems that there might be a bug in FFMpegCore, causing VerifyFFMpegExists() to
// fail before it can throw the correct exception, which causes a different exception.
Log.Fatal("FFMpeg verification resulted in a failure:\n{Message}", e);
Log.Fatal("FFMpeg verification resulted in a failure: {Message}", e);
Environment.ExitCode = 1;
return false;
}
Expand Down

0 comments on commit 3c54763

Please sign in to comment.