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; }