Skip to content

Migrate to the Options Pattern #240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<PropertyGroup>
<AnalysisLevel>latest-all</AnalysisLevel>
<Configurations>Debug;Release;Docker;Docker-debug</Configurations>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<ImplicitUsings>disable</ImplicitUsings>
Expand Down
4 changes: 2 additions & 2 deletions src/AzzyBot.Bot/Commands/AdminCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ namespace AzzyBot.Bot.Commands;
public sealed class AdminCommands
{
[Command("admin"), RequireGuild, RequireApplicationOwner, RequirePermissions(BotPermissions = [], UserPermissions = [DiscordPermission.Administrator])]
public sealed class AdminGroup(ILogger<AdminGroup> logger, AzzyBotSettingsRecord settings, DbActions dbActions, DiscordBotService botService)
public sealed class AdminGroup(ILogger<AdminGroup> logger, AzzyBotSettings settings, DbActions dbActions, DiscordBotService botService)
{
private readonly ILogger<AdminGroup> _logger = logger;
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly AzzyBotSettings _settings = settings;
private readonly DbActions _dbActions = dbActions;
private readonly DiscordBotService _botService = botService;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

namespace AzzyBot.Bot.Commands.Autocompletes;

public sealed class AzzyHelpAutocomplete(AzzyBotSettingsRecord settings) : IAutoCompleteProvider
public sealed class AzzyHelpAutocomplete(AzzyBotSettings settings) : IAutoCompleteProvider
{
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly AzzyBotSettings _settings = settings;

public ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync(AutoCompleteContext context)
{
Expand Down
4 changes: 2 additions & 2 deletions src/AzzyBot.Bot/Commands/Autocompletes/GuildsAutocomplete.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@

namespace AzzyBot.Bot.Commands.Autocompletes;

public sealed class GuildsAutocomplete(AzzyBotSettingsRecord settings, DiscordBotService botService) : IAutoCompleteProvider
public sealed class GuildsAutocomplete(AzzyBotSettings settings, DiscordBotService botService) : IAutoCompleteProvider
{
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly AzzyBotSettings _settings = settings;
private readonly DiscordBotService _botService = botService;

public ValueTask<IEnumerable<DiscordAutoCompleteChoice>> AutoCompleteAsync(AutoCompleteContext context)
Expand Down
8 changes: 4 additions & 4 deletions src/AzzyBot.Bot/Commands/CoreCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ namespace AzzyBot.Bot.Commands;
public sealed class CoreCommands
{
[Command("core"), RequireGuild]
public sealed class CoreGroup(ILogger<CoreGroup> logger, AzzyBotSettingsRecord settings, DbActions dbActions, DiscordBotService botService)
public sealed class CoreGroup(ILogger<CoreGroup> logger, AzzyBotSettings settings, DbActions dbActions, DiscordBotService botService)
{
private readonly ILogger<CoreGroup> _logger = logger;
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly AzzyBotSettings _settings = settings;
private readonly DbActions _dbActions = dbActions;
private readonly DiscordBotService _botService = botService;

Expand Down Expand Up @@ -110,9 +110,9 @@ public async ValueTask HelpAsync
}

[Command("stats")]
public sealed class CoreStats(AppStatsRecord stats, ILogger<CoreStats> logger)
public sealed class CoreStats(AppStats stats, ILogger<CoreStats> logger)
{
private readonly AppStatsRecord _stats = stats;
private readonly AppStats _stats = stats;
private readonly ILogger<CoreStats> _logger = logger;

[Command("hardware"), Description("Shows information about the hardware side of the bot.")]
Expand Down
31 changes: 31 additions & 0 deletions src/AzzyBot.Bot/Extensions/IConfigurationManagerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.IO;
using Microsoft.Extensions.Configuration;

namespace AzzyBot.Bot.Extensions;

public static class IConfigurationManagerExtensions
{
public static void AddAppConfiguration(this IConfigurationManager configurationManager, bool isDev, bool isDocker)
{
string settingsFile = "AzzyBotSettings.json";
if (isDev)
{
settingsFile = "AzzyBotSettings-Dev.json";
}
else if (isDocker)
{
settingsFile = "AzzyBotSettings-Docker.json";
}

string path = Path.Combine("Settings", settingsFile);

configurationManager.AddJsonFile(path);

if (isDev)
return;

path = Path.Combine("Modules", "Core", "Files", "AppStats.json");

configurationManager.AddJsonFile(path);
}
}
113 changes: 34 additions & 79 deletions src/AzzyBot.Bot/Extensions/IServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using AzzyBot.Bot.Commands;
using AzzyBot.Bot.Commands.Checks;
using AzzyBot.Bot.Commands.Converters;
Expand All @@ -9,9 +7,11 @@
using AzzyBot.Bot.Services.DiscordEvents;
using AzzyBot.Bot.Services.Modules;
using AzzyBot.Bot.Settings;
using AzzyBot.Core.Settings;
using AzzyBot.Bot.Settings.Validators;
using AzzyBot.Core.Utilities;
using AzzyBot.Core.Utilities.Records;
using AzzyBot.Data.Extensions;
using AzzyBot.Data.Settings;
using DSharpPlus;
using DSharpPlus.Commands;
using DSharpPlus.Commands.Processors.SlashCommands;
Expand All @@ -21,9 +21,9 @@
using DSharpPlus.Interactivity.Enums;
using DSharpPlus.Interactivity.Extensions;
using Lavalink4NET.Extensions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using NCronJob;

namespace AzzyBot.Bot.Extensions;
Expand All @@ -33,18 +33,20 @@ public static class IServiceCollectionExtensions
public static void AzzyBotServices(this IServiceCollection services, bool isDev, bool isDocker, int logDays = 7)
{
IServiceProvider serviceProvider = services.BuildServiceProvider();
AzzyBotSettingsRecord settings = serviceProvider.GetRequiredService<AzzyBotSettingsRecord>();
DatabaseSettings dbSettings = serviceProvider.GetRequiredService<IOptions<DatabaseSettings>>().Value;
AzzyBotSettings botSettings = serviceProvider.GetRequiredService<IOptions<AzzyBotSettings>>().Value;
MusicStreamingSettings musicSettings = serviceProvider.GetRequiredService<IOptions<MusicStreamingSettings>>().Value;

// Need to register as Singleton first
// Otherwise DI doesn't work properly
services.AddSingleton<CoreServiceHost>();
services.AddHostedService(static s => s.GetRequiredService<CoreServiceHost>());

// Register the database services
services.AzzyBotDataServices(isDev, settings.Database!.EncryptionKey, settings.Database.Host, settings.Database.Port, settings.Database.User, settings.Database.Password, settings.Database.DatabaseName);
services.AzzyBotDataServices(isDev, dbSettings.EncryptionKey, dbSettings.Host, dbSettings.Port, dbSettings.User, dbSettings.Password, dbSettings.DatabaseName);

services.DiscordClient(settings.BotToken);
services.DiscordClientCommands(settings);
services.DiscordClient(botSettings.BotToken);
services.DiscordClientCommands(botSettings);
services.DiscordClientInteractivity();

services.AddSingleton<DiscordBotService>();
Expand All @@ -70,23 +72,23 @@ public static void AzzyBotServices(this IServiceCollection services, bool isDev,
services.ConfigureLavalink(config =>
{
Uri baseAddress = (isDocker) ? new("http://AzzyBot-Ms:2333") : new("http://localhost:2333");
if (settings.MusicStreaming is not null)
if (musicSettings is not null)
{
if (!string.IsNullOrWhiteSpace(settings.MusicStreaming.LavalinkHost) && settings.MusicStreaming.LavalinkPort is not 0)
if (!string.IsNullOrWhiteSpace(musicSettings.LavalinkHost) && musicSettings.LavalinkPort is not 0)
{
baseAddress = new($"http://{settings.MusicStreaming.LavalinkHost}:{settings.MusicStreaming.LavalinkPort}");
baseAddress = new($"http://{musicSettings.LavalinkHost}:{musicSettings.LavalinkPort}");
}
else if (!string.IsNullOrWhiteSpace(settings.MusicStreaming.LavalinkHost) && settings.MusicStreaming.LavalinkPort is 0)
else if (!string.IsNullOrWhiteSpace(musicSettings.LavalinkHost) && musicSettings.LavalinkPort is 0)
{
baseAddress = new($"http://{settings.MusicStreaming.LavalinkHost}:2333");
baseAddress = new($"http://{musicSettings.LavalinkHost}:2333");
}
else if (string.IsNullOrWhiteSpace(settings.MusicStreaming.LavalinkHost) && settings.MusicStreaming.LavalinkPort is not 0)
else if (string.IsNullOrWhiteSpace(musicSettings.LavalinkHost) && musicSettings.LavalinkPort is not 0)
{
baseAddress = (isDocker) ? new($"http://AzzyBot-Ms:{settings.MusicStreaming.LavalinkPort}") : new($"http://localhost:{settings.MusicStreaming.LavalinkPort}");
baseAddress = (isDocker) ? new($"http://AzzyBot-Ms:{musicSettings.LavalinkPort}") : new($"http://localhost:{musicSettings.LavalinkPort}");
}

if (!string.IsNullOrWhiteSpace(settings.MusicStreaming.LavalinkPassword))
config.Passphrase = settings.MusicStreaming.LavalinkPassword;
if (!string.IsNullOrWhiteSpace(musicSettings.LavalinkPassword))
config.Passphrase = musicSettings.LavalinkPassword;
}

config.BaseAddress = baseAddress;
Expand All @@ -97,68 +99,21 @@ public static void AzzyBotServices(this IServiceCollection services, bool isDev,
services.AddSingleton<MusicStreamingService>();
}

public static void AzzyBotSettings(this IServiceCollection services, bool isDev, bool isDocker)
public static void AddAppSettings(this IServiceCollection services)
{
string settingsFile = "AzzyBotSettings.json";
if (isDev)
{
settingsFile = "AzzyBotSettings-Dev.json";
}
else if (isDocker)
{
settingsFile = "AzzyBotSettings-Docker.json";
}

string path = Path.Combine("Settings", settingsFile);

AzzyBotSettingsRecord? settings = Misc.GetConfiguration(path).Get<AzzyBotSettingsRecord>();
if (settings is null)
{
Console.Error.Write("No bot configuration found! Please set your settings.");
if (!HardwareStats.CheckIfLinuxOs)
Console.ReadKey();

Environment.Exit(1);
}

settings.SettingsFile = path;

// Check settings if something is missing
List<string> exclusions = new(11)
{
nameof(settings.Database.NewEncryptionKey),
nameof(settings.DiscordStatus.StreamUrl),
nameof(settings.MusicStreaming),
nameof(settings.MusicStreaming.LavalinkHost),
nameof(settings.MusicStreaming.LavalinkPort),
nameof(settings.MusicStreaming.LavalinkPassword)
};

if (isDocker)
{
exclusions.Add(nameof(settings.Database.Host));
exclusions.Add(nameof(settings.Database.Port));
exclusions.Add(nameof(settings.Database.User));
exclusions.Add(nameof(settings.Database.Password));
exclusions.Add(nameof(settings.Database.DatabaseName));
}
else
{
exclusions.Add(nameof(settings.Database.Password));
}

SettingsCheck.CheckSettings(settings, exclusions);

if (settings.Database!.EncryptionKey.Length is not 32)
{
Console.Error.WriteLine($"The {nameof(settings.Database.EncryptionKey)} must contain exactly 32 characters!");
if (!HardwareStats.CheckIfLinuxOs)
Console.ReadKey();

Environment.Exit(1);
}

services.AddSingleton(settings);
services.AddSingleton<IValidateOptions<AzzyBotSettings>, AzzyBotSettingsValidator>().AddOptionsWithValidateOnStart<AzzyBotSettings>();
services.AddSingleton<IValidateOptions<DatabaseSettings>, DatabaseSettingsValidator>().AddOptionsWithValidateOnStart<DatabaseSettings>();
services.AddSingleton<IValidateOptions<DiscordStatusSettings>, DiscordStatusSettingsValidator>().AddOptionsWithValidateOnStart<DiscordStatusSettings>();
services.AddSingleton<IValidateOptions<MusicStreamingSettings>, MusicStreamingSettingsValidator>().AddOptionsWithValidateOnStart<MusicStreamingSettings>();
services.AddSingleton<IValidateOptions<CoreUpdaterSettings>, CoreUpdaterValidator>().AddOptionsWithValidateOnStart<CoreUpdaterSettings>();
services.AddSingleton<IValidateOptions<AppStats>, AppStatsValidator>().AddOptionsWithValidateOnStart<AppStats>();

services.AddOptions<AzzyBotSettings>().BindConfiguration(nameof(AzzyBotSettings));
services.AddOptions<DatabaseSettings>().BindConfiguration(nameof(DatabaseSettings));
services.AddOptions<DiscordStatusSettings>().BindConfiguration(nameof(DiscordStatusSettings));
services.AddOptions<MusicStreamingSettings>().BindConfiguration(nameof(MusicStreamingSettings));
services.AddOptions<CoreUpdaterSettings>().BindConfiguration(nameof(CoreUpdaterSettings));
services.AddOptions<AppStats>().BindConfiguration(nameof(AppStats));
}

private static void DiscordClient(this IServiceCollection services, string token)
Expand All @@ -167,7 +122,7 @@ private static void DiscordClient(this IServiceCollection services, string token
services.ConfigureEventHandlers(static e => e.AddEventHandlers<DiscordGuildsHandler>(ServiceLifetime.Singleton));
}

private static void DiscordClientCommands(this IServiceCollection services, AzzyBotSettingsRecord settings)
private static void DiscordClientCommands(this IServiceCollection services, AzzyBotSettings settings)
{
services.AddSingleton<DiscordCommandsErrorHandler>();
services.AddCommandsExtension((IServiceProvider sp, CommandsExtension c) =>
Expand Down
2 changes: 2 additions & 0 deletions src/AzzyBot.Bot/Modules/Core/Files/AppStats.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"AppStats": {
"Commit": "Commit not found",
"CompilationDate": "Compilation date not found",
"LocCS": "Lines of source code not found"
}
}
5 changes: 3 additions & 2 deletions src/AzzyBot.Bot/Services/DiscordBotService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@
using DSharpPlus.Entities;
using DSharpPlus.Exceptions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace AzzyBot.Bot.Services;

public sealed class DiscordBotService(ILogger<DiscordBotService> logger, AzzyBotSettingsRecord settings, DbActions dbActions, DiscordClient client)
public sealed class DiscordBotService(ILogger<DiscordBotService> logger, IOptions<AzzyBotSettings> settings, DbActions dbActions, DiscordClient client)
{
private readonly ILogger<DiscordBotService> _logger = logger;
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly AzzyBotSettings _settings = settings.Value;
private readonly DbActions _dbActions = dbActions;
private readonly DiscordClient _client = client;
private const string BugReportMessage = "Send a [bug report]([BugReportUri]) to help us fixing this issue!\nPlease include a screenshot of this exception embed and the attached StackTrace file.\nYour Contribution is very welcome.";
Expand Down
9 changes: 5 additions & 4 deletions src/AzzyBot.Bot/Services/DiscordBotServiceHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
using DSharpPlus.Entities;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace AzzyBot.Bot.Services;

public sealed class DiscordBotServiceHost(ILogger<DiscordBotServiceHost> logger, AzzyBotSettingsRecord settings, DiscordClient client) : IHostedService
public sealed class DiscordBotServiceHost(ILogger<DiscordBotServiceHost> logger, IOptions<DiscordStatusSettings> settings, DiscordClient client) : IHostedService
{
private readonly ILogger<DiscordBotServiceHost> _logger = logger;
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly DiscordStatusSettings _settings = settings.Value;
private readonly DiscordClient _client = client;

public async Task StartAsync(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

DiscordActivity activity = DiscordBotService.SetBotStatusActivity(_settings.DiscordStatus?.Activity ?? 2, _settings.DiscordStatus?.Doing ?? "Music", _settings.DiscordStatus?.StreamUrl);
DiscordUserStatus status = DiscordBotService.SetBotStatusUserStatus(_settings.DiscordStatus?.Status ?? 1);
DiscordActivity activity = DiscordBotService.SetBotStatusActivity(_settings.Activity, _settings.Doing, _settings.StreamUrl);
DiscordUserStatus status = DiscordBotService.SetBotStatusUserStatus(_settings.Status);

await _client.ConnectAsync(activity, status);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
using DSharpPlus.Entities;
using DSharpPlus.EventArgs;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace AzzyBot.Bot.Services.DiscordEvents;

public sealed class DiscordGuildsHandler(ILogger<DiscordGuildsHandler> logger, AzzyBotSettingsRecord settings, DiscordBotService botService, DbActions dbActions) : IEventHandler<GuildCreatedEventArgs>, IEventHandler<GuildDeletedEventArgs>, IEventHandler<GuildDownloadCompletedEventArgs>
public sealed class DiscordGuildsHandler(ILogger<DiscordGuildsHandler> logger, IOptions<AzzyBotSettings> settings, DiscordBotService botService, DbActions dbActions) : IEventHandler<GuildCreatedEventArgs>, IEventHandler<GuildDeletedEventArgs>, IEventHandler<GuildDownloadCompletedEventArgs>
{
private readonly ILogger<DiscordGuildsHandler> _logger = logger;
private readonly AzzyBotSettingsRecord _settings = settings;
private readonly AzzyBotSettings _settings = settings.Value;
private readonly DbActions _dbActions = dbActions;
private readonly DiscordBotService _botService = botService;

Expand Down
Loading