diff --git a/VPLink/Configuration/BotConfiguration.cs b/VPLink/Configuration/BotConfiguration.cs
new file mode 100644
index 0000000..c1b92e5
--- /dev/null
+++ b/VPLink/Configuration/BotConfiguration.cs
@@ -0,0 +1,33 @@
+namespace VPLink.Configuration;
+
+///
+/// Represents the bot configuration.
+///
+public sealed class BotConfiguration
+{
+ ///
+ /// Gets or sets a value indicating whether the bot should announce avatar events.
+ ///
+ ///
+ /// if the bot should announce avatar events; otherwise, .
+ ///
+ public bool AnnounceAvatarEvents { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether the bot should announce avatar events for bots.
+ ///
+ ///
+ /// if the bot should announce avatar events for bots; otherwise,
+ /// .
+ ///
+ public bool AnnounceBots { get; set; } = false;
+
+ ///
+ /// Gets or sets a value indicating whether the bot should relay messages from other bots.
+ ///
+ ///
+ /// if the bot should relay messages from other bots; otherwise,
+ /// .
+ ///
+ public bool RelayBotMessages { get; set; } = false;
+}
diff --git a/VPLink/Configuration/DiscordConfiguration.cs b/VPLink/Configuration/DiscordConfiguration.cs
new file mode 100644
index 0000000..e9b473a
--- /dev/null
+++ b/VPLink/Configuration/DiscordConfiguration.cs
@@ -0,0 +1,19 @@
+namespace VPLink.Configuration;
+
+///
+/// Represents the Discord configuration.
+///
+public sealed class DiscordConfiguration
+{
+ ///
+ /// Gets or sets the channel ID to which the bot should relay messages.
+ ///
+ /// The channel ID.
+ public ulong ChannelId { get; set; }
+
+ ///
+ /// Gets or sets the Discord token.
+ ///
+ /// The Discord token.
+ public string Token { get; set; } = string.Empty;
+}
diff --git a/VPLink/Configuration/VirtualParadiseConfiguration.cs b/VPLink/Configuration/VirtualParadiseConfiguration.cs
new file mode 100644
index 0000000..e806157
--- /dev/null
+++ b/VPLink/Configuration/VirtualParadiseConfiguration.cs
@@ -0,0 +1,31 @@
+namespace VPLink.Configuration;
+
+///
+/// Represents the Virtual Paradise configuration.
+///
+public sealed class VirtualParadiseConfiguration
+{
+ ///
+ /// Gets or sets the display name of the bot.
+ ///
+ /// The display name.
+ public string BotName { get; set; } = "VPLink";
+
+ ///
+ /// Gets or sets the password with which to log in to Virtual Paradise.
+ ///
+ /// The login password.
+ public string Password { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the username with which to log in to Virtual Paradise.
+ ///
+ /// The login username.
+ public string Username { get; set; } = string.Empty;
+
+ ///
+ /// Gets or sets the world into which the bot should enter.
+ ///
+ /// The world to enter.
+ public string World { get; set; } = string.Empty;
+}
diff --git a/VPLink/Program.cs b/VPLink/Program.cs
index 2fc3c2e..c4cdbef 100644
--- a/VPLink/Program.cs
+++ b/VPLink/Program.cs
@@ -25,6 +25,7 @@ builder.Logging.ClearProviders();
builder.Logging.AddSerilog();
builder.Services.AddSingleton();
+builder.Services.AddSingleton();
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/VPLink/Services/ConfigurationService.cs b/VPLink/Services/ConfigurationService.cs
new file mode 100644
index 0000000..1c30c18
--- /dev/null
+++ b/VPLink/Services/ConfigurationService.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.Configuration;
+using VPLink.Configuration;
+
+namespace VPLink.Services;
+
+///
+internal sealed class ConfigurationService : IConfigurationService
+{
+ private readonly IConfiguration _configuration;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ public ConfigurationService(IConfiguration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ ///
+ public BotConfiguration BotConfiguration
+ {
+ get => _configuration.GetSection("Bot").Get()!;
+ }
+
+ ///
+ public DiscordConfiguration DiscordConfiguration
+ {
+ get => _configuration.GetSection("Discord").Get()!;
+ }
+
+ ///
+ public VirtualParadiseConfiguration VirtualParadiseConfiguration
+ {
+ get => _configuration.GetSection("VirtualParadise").Get()!;
+ }
+}
diff --git a/VPLink/Services/DiscordService.cs b/VPLink/Services/DiscordService.cs
index eec3982..071bdc5 100644
--- a/VPLink/Services/DiscordService.cs
+++ b/VPLink/Services/DiscordService.cs
@@ -5,10 +5,10 @@ using System.Text.RegularExpressions;
using Discord;
using Discord.Interactions;
using Discord.WebSocket;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using VPLink.Commands;
+using VPLink.Configuration;
using VpSharp.Entities;
namespace VPLink.Services;
@@ -21,7 +21,7 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
private readonly ILogger _logger;
private readonly IServiceProvider _serviceProvider;
- private readonly IConfiguration _configuration;
+ private readonly IConfigurationService _configurationService;
private readonly InteractionService _interactionService;
private readonly DiscordSocketClient _discordClient;
private readonly Subject _messageReceived = new();
@@ -31,18 +31,18 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
///
/// The logger.
/// The service provider.
- /// The configuration.
+ /// The configuration service.
/// The interaction service.
/// The Discord client.
public DiscordService(ILogger logger,
IServiceProvider serviceProvider,
- IConfiguration configuration,
+ IConfigurationService configurationService,
InteractionService interactionService,
DiscordSocketClient discordClient)
{
_logger = logger;
_serviceProvider = serviceProvider;
- _configuration = configuration;
+ _configurationService = configurationService;
_interactionService = interactionService;
_discordClient = discordClient;
}
@@ -62,8 +62,8 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
_discordClient.InteractionCreated += OnInteractionCreated;
_discordClient.MessageReceived += OnDiscordMessageReceived;
- string token = _configuration.GetSection("Discord:Token").Value ??
- throw new InvalidOperationException("Token is not set.");
+ DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
+ string token = configuration.Token ?? throw new InvalidOperationException("Token is not set.");
_logger.LogDebug("Connecting to Discord");
await _discordClient.LoginAsync(TokenType.Bot, token);
@@ -75,7 +75,8 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
if (arg is not IUserMessage message)
return Task.CompletedTask;
- if (message.Channel.Id != _configuration.GetSection("Discord:ChannelId").Get())
+ DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
+ if (message.Channel.Id != configuration.ChannelId)
return Task.CompletedTask;
_messageReceived.OnNext(message);
@@ -153,7 +154,7 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
return Task.CompletedTask;
}
- if (author.IsBot && !_configuration.GetSection("Bot:RelayBotMessages").Get())
+ if (author.IsBot && !_configurationService.BotConfiguration.RelayBotMessages)
{
_logger.LogDebug("Bot messages are disabled, ignoring message");
return Task.CompletedTask;
@@ -172,7 +173,9 @@ internal sealed partial class DiscordService : BackgroundService, IDiscordServic
private bool TryGetRelayChannel([NotNullWhen(true)] out ITextChannel? channel)
{
- var channelId = _configuration.GetValue("Discord:ChannelId");
+ DiscordConfiguration configuration = _configurationService.DiscordConfiguration;
+ ulong channelId = configuration.ChannelId;
+
if (_discordClient.GetChannel(channelId) is ITextChannel textChannel)
{
channel = textChannel;
diff --git a/VPLink/Services/IConfigurationService.cs b/VPLink/Services/IConfigurationService.cs
new file mode 100644
index 0000000..c8d657e
--- /dev/null
+++ b/VPLink/Services/IConfigurationService.cs
@@ -0,0 +1,27 @@
+using VPLink.Configuration;
+
+namespace VPLink.Services;
+
+///
+/// Represents the configuration service.
+///
+public interface IConfigurationService
+{
+ ///
+ /// Gets the bot configuration.
+ ///
+ /// The bot configuration.
+ BotConfiguration BotConfiguration { get; }
+
+ ///
+ /// Gets the Discord configuration.
+ ///
+ /// The Discord configuration.
+ DiscordConfiguration DiscordConfiguration { get; }
+
+ ///
+ /// Gets the Virtual Paradise configuration.
+ ///
+ /// The Virtual Paradise configuration.
+ VirtualParadiseConfiguration VirtualParadiseConfiguration { get; }
+}
diff --git a/VPLink/Services/VirtualParadiseService.cs b/VPLink/Services/VirtualParadiseService.cs
index c459ffe..f28bc03 100644
--- a/VPLink/Services/VirtualParadiseService.cs
+++ b/VPLink/Services/VirtualParadiseService.cs
@@ -1,12 +1,13 @@
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Discord;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using VPLink.Configuration;
using VpSharp;
using VpSharp.Entities;
using Color = System.Drawing.Color;
+using VirtualParadiseConfiguration = VPLink.Configuration.VirtualParadiseConfiguration;
namespace VPLink.Services;
@@ -14,7 +15,7 @@ namespace VPLink.Services;
internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadiseService
{
private readonly ILogger _logger;
- private readonly IConfiguration _configuration;
+ private readonly IConfigurationService _configurationService;
private readonly VirtualParadiseClient _virtualParadiseClient;
private readonly Subject _messageReceived = new();
private readonly Subject _avatarJoined = new();
@@ -24,14 +25,14 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
/// Initializes a new instance of the class.
///
/// The logger.
- /// The configuration.
+ /// The configuration service.
/// The Virtual Paradise client.
public VirtualParadiseService(ILogger logger,
- IConfiguration configuration,
+ IConfigurationService configurationService,
VirtualParadiseClient virtualParadiseClient)
{
_logger = logger;
- _configuration = configuration;
+ _configurationService = configurationService;
_virtualParadiseClient = virtualParadiseClient;
}
@@ -50,7 +51,7 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
if (message is null) throw new ArgumentNullException(nameof(message));
if (string.IsNullOrWhiteSpace(message.Content)) return Task.CompletedTask;
- if (message.Author.IsBot && !_configuration.GetSection("Bot:RelayBotMessages").Get())
+ if (message.Author.IsBot && !_configurationService.BotConfiguration.RelayBotMessages)
{
_logger.LogDebug("Bot messages are disabled, ignoring message");
return Task.CompletedTask;
@@ -59,7 +60,8 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
_logger.LogInformation("Message by {Author}: {Content}", message.Author, message.Content);
string displayName = message.Author.GlobalName ?? message.Author.Username;
- return _virtualParadiseClient.SendMessageAsync(displayName, message.Content, FontStyle.Bold, Color.MidnightBlue);
+ return _virtualParadiseClient.SendMessageAsync(displayName, message.Content, FontStyle.Bold,
+ Color.MidnightBlue);
}
///
@@ -70,14 +72,12 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
_virtualParadiseClient.AvatarJoined.Subscribe(OnVirtualParadiseAvatarJoined);
_virtualParadiseClient.AvatarLeft.Subscribe(OnVirtualParadiseAvatarLeft);
- string username = _configuration.GetSection("VirtualParadise:Username").Value ??
- throw new InvalidOperationException("Username is not set.");
- string password = _configuration.GetSection("VirtualParadise:Password").Value ??
- throw new InvalidOperationException("Password is not set.");
- string world = _configuration.GetSection("VirtualParadise:World").Value ??
- throw new InvalidOperationException("World is not set.");
- string botName = _configuration.GetSection("VirtualParadise:BotName").Value ??
- throw new InvalidOperationException("Bot name is not set.");
+ VirtualParadiseConfiguration configuration = _configurationService.VirtualParadiseConfiguration;
+
+ string username = configuration.Username ?? throw new InvalidOperationException("Username is not set.");
+ string password = configuration.Password ?? throw new InvalidOperationException("Password is not set.");
+ string world = configuration.World ?? throw new InvalidOperationException("World is not set.");
+ string botName = configuration.BotName ?? throw new InvalidOperationException("Bot name is not set.");
_logger.LogDebug("Connecting to Virtual Paradise");
await _virtualParadiseClient.ConnectAsync().ConfigureAwait(false);
@@ -89,15 +89,10 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
private void OnVirtualParadiseAvatarJoined(VirtualParadiseAvatar avatar)
{
- if (!_configuration.GetValue("Bot:AnnounceAvatarEvents"))
- {
- _logger.LogDebug("Join/leave events are disabled, ignoring event");
- return;
- }
+ BotConfiguration configuration = _configurationService.BotConfiguration;
- if (avatar.IsBot && !_configuration.GetSection("Bot:AnnounceBots").Get())
+ if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots)
{
- _logger.LogDebug("Bot events are disabled, ignoring event");
return;
}
@@ -106,15 +101,10 @@ internal sealed class VirtualParadiseService : BackgroundService, IVirtualParadi
private void OnVirtualParadiseAvatarLeft(VirtualParadiseAvatar avatar)
{
- if (!_configuration.GetValue("Bot:AnnounceAvatarEvents"))
- {
- _logger.LogDebug("Join/leave events are disabled, ignoring event");
- return;
- }
+ BotConfiguration configuration = _configurationService.BotConfiguration;
- if (avatar.IsBot && !_configuration.GetSection("Bot:AnnounceBots").Get())
+ if (!configuration.AnnounceAvatarEvents || avatar.IsBot && !configuration.AnnounceBots)
{
- _logger.LogDebug("Bot events are disabled, ignoring event");
return;
}