diff --git a/VPLink/Commands/InfoCommand.cs b/VPLink/Commands/InfoCommand.cs
new file mode 100644
index 0000000..6f34be0
--- /dev/null
+++ b/VPLink/Commands/InfoCommand.cs
@@ -0,0 +1,57 @@
+using System.Text;
+using Discord;
+using Discord.Interactions;
+using Discord.WebSocket;
+using VPLink.Services;
+
+namespace VPLink.Commands;
+
+///
+/// Represents a class which implements the info command.
+///
+internal sealed class InfoCommand : InteractionModuleBase
+{
+ private readonly BotService _botService;
+ private readonly DiscordSocketClient _discordClient;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The bot service.
+ ///
+ public InfoCommand(BotService botService, DiscordSocketClient discordClient)
+ {
+ _botService = botService;
+ _discordClient = discordClient;
+ }
+
+ [SlashCommand("info", "Displays information about the bot.")]
+ [RequireContext(ContextType.Guild)]
+ public async Task InfoAsync()
+ {
+ SocketGuildUser member = Context.Guild.GetUser(_discordClient.CurrentUser.Id);
+ string pencilVersion = _botService.Version;
+
+ SocketRole? highestRole = member.Roles.Where(r => r.Color != Color.Default).MaxBy(r => r.Position);
+
+ var embed = new EmbedBuilder();
+ embed.WithAuthor(member);
+ embed.WithColor(highestRole?.Color ?? Color.Default);
+ embed.WithThumbnailUrl(member.GetAvatarUrl());
+ embed.WithTitle($"VPLink v{pencilVersion}");
+ embed.WithDescription("Created by <@94248427663130624>, hosted [on GitHub](https://github.com/oliverbooth/VPLink).");
+ embed.AddField("Ping", $"{_discordClient.Latency} ms", true);
+ embed.AddField("Started", $"", true);
+
+ var builder = new StringBuilder();
+ builder.AppendLine($"VPLink: {pencilVersion}");
+ builder.AppendLine($"Discord.Net: {_botService.DiscordNetVersion}");
+ builder.AppendLine($"VP#: {_botService.VpSharpVersion}");
+ builder.AppendLine($"CLR: {Environment.Version.ToString(3)}");
+ builder.AppendLine($"Host: {Environment.OSVersion}");
+
+ embed.AddField("Version", $"```\n{builder}\n```");
+
+ await RespondAsync(embed: embed.Build(), ephemeral: true).ConfigureAwait(false);
+ }
+}
diff --git a/VPLink/Program.cs b/VPLink/Program.cs
index 2cb9a15..621a79f 100644
--- a/VPLink/Program.cs
+++ b/VPLink/Program.cs
@@ -25,6 +25,8 @@ builder.Configuration.AddTomlFile("data/config.toml", true, true);
builder.Logging.ClearProviders();
builder.Logging.AddSerilog();
+builder.Services.AddHostedSingleton();
+
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/VPLink/Services/BotService.cs b/VPLink/Services/BotService.cs
new file mode 100644
index 0000000..52c558e
--- /dev/null
+++ b/VPLink/Services/BotService.cs
@@ -0,0 +1,61 @@
+using System.Reflection;
+using Discord.WebSocket;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using VpSharp;
+
+namespace VPLink.Services;
+
+internal sealed class BotService : BackgroundService
+{
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger.
+ public BotService(ILogger logger)
+ {
+ _logger = logger;
+ var attribute = typeof(BotService).Assembly.GetCustomAttribute();
+ Version = attribute?.InformationalVersion ?? "Unknown";
+
+ attribute = typeof(DiscordSocketClient).Assembly.GetCustomAttribute();
+ DiscordNetVersion = attribute?.InformationalVersion ?? "Unknown";
+
+ attribute = typeof(VirtualParadiseClient).Assembly.GetCustomAttribute();
+ VpSharpVersion = attribute?.InformationalVersion ?? "Unknown";
+ }
+
+ ///
+ /// Gets the Discord.Net version.
+ ///
+ /// The Discord.Net version.
+ public string DiscordNetVersion { get; }
+
+ ///
+ /// Gets the date and time at which the bot was started.
+ ///
+ /// The start timestamp.
+ public DateTimeOffset StartedAt { get; private set; }
+
+ ///
+ /// Gets the bot version.
+ ///
+ /// The bot version.
+ public string Version { get; }
+
+ ///
+ /// Gets the VP# version.
+ ///
+ /// The VP# version.
+ public string VpSharpVersion { get; }
+
+ ///
+ protected override Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ StartedAt = DateTimeOffset.UtcNow;
+ _logger.LogInformation("VPLink v{Version} is starting...", Version);
+ return Task.CompletedTask;
+ }
+}
diff --git a/VPLink/Services/DiscordService.cs b/VPLink/Services/DiscordService.cs
index 530bf02..1ba757d 100644
--- a/VPLink/Services/DiscordService.cs
+++ b/VPLink/Services/DiscordService.cs
@@ -44,6 +44,7 @@ internal sealed class DiscordService : BackgroundService
_logger.LogInformation("Establishing relay");
_logger.LogInformation("Adding command modules");
+ await _interactionService.AddModuleAsync(_serviceProvider).ConfigureAwait(false);
await _interactionService.AddModuleAsync(_serviceProvider).ConfigureAwait(false);
_discordClient.Ready += OnReady;