diff --git a/MinecraftDiscordBot/BotConfiguration.cs b/MinecraftDiscordBot/BotConfiguration.cs index 34f7f21..3806874 100644 --- a/MinecraftDiscordBot/BotConfiguration.cs +++ b/MinecraftDiscordBot/BotConfiguration.cs @@ -26,6 +26,9 @@ public class BotConfiguration : IBotConfiguration, IBotConfigurator { [JsonProperty("admins", Required = Required.DisallowNull)] [Option("admins", Default = new ulong[] { }, HelpText = "The list of bot administrators.")] public ulong[] Administrators { get; init; } = Array.Empty(); + [JsonProperty("logchannel", Required = Required.DisallowNull)] + [Option("logchannel", Default = null, HelpText = "Optionally the id of a channel to mirror log to.")] + public ulong? LogChannel { get; init; } = null; [JsonIgnore] public BotConfiguration Config => this; } diff --git a/MinecraftDiscordBot/Program.cs b/MinecraftDiscordBot/Program.cs index 44609c2..5286b5d 100644 --- a/MinecraftDiscordBot/Program.cs +++ b/MinecraftDiscordBot/Program.cs @@ -31,9 +31,11 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana public IEnumerable Channels => _channels ?? throw new InvalidProgramException("Channels used before verification!"); public ActiveChannel[]? _channels; private bool disposedValue; + private static ITextChannel? LogChannel; private readonly RootCommandService _computer; public static bool OnlineNotifications => true; + public const LogSeverity DiscordLogSeverity = LogSeverity.Warning; private const string ClientScriptName = "MinecraftDiscordBot.ClientScript.lua"; private const string WebhookName = "minecraftbot"; public readonly string ClientScript; @@ -70,11 +72,10 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana _whitelistedChannels = config.Channels.ToHashSet(); } - private void ComputerConnectedChanged(object? sender, IWebSocketConnection? e) { - _ = Task.Run(() => Broadcast(i => i.SendMessageAsync(e is not null - ? "The Minecraft client is now available!" - : "The Minecraft client disconnected!"))); - } + private void ComputerConnectedChanged(object? sender, IWebSocketConnection? e) + => _ = Task.Run(() => Broadcast(i => i.SendMessageAsync(e is not null + ? "The Minecraft client is now available!" + : "The Minecraft client disconnected!"))); private void MinecraftMessageReceived(object? sender, ChatEvent e) => Task.Run(() => WebhookBroadcast(i => i.SendMessageAsync(e.Message, username: e.Username, avatarUrl: $"https://crafatar.com/renders/head/{e.UUID}"))); private Task WebhookBroadcast(Func> apply) => Task.WhenAll(Channels.Select(i => apply(new DiscordWebhookClient(i.Webhook)))); @@ -109,7 +110,12 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana } private async Task HasValidChannels() { - if (await GetValidChannels(_whitelistedChannels).ToArrayAsync() is not { Length: > 0 } channels) { + if (_config.LogChannel is ulong logChannelId) { + LogChannel = await IsValidChannel(logChannelId); + if (LogChannel is null) + await LogWarningAsync(BotSource, $"The given log channel ID is not valid '{logChannelId}'!"); + } + if (await GetValidChannels(_whitelistedChannels) is not { Length: > 0 } channels) { await LogErrorAsync(BotSource, new InvalidOperationException("No valid textchannel was whitelisted!")); return false; } @@ -127,23 +133,23 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana socket.OnMessage = async message => await SocketReceived(socket, message); }); - private async IAsyncEnumerable GetValidChannels(IEnumerable ids) { - foreach (var channelId in ids) { - var channel = await _client.GetChannelAsync(channelId); - if (channel is not ITextChannel textChannel) { - if (channel is null) await LogWarningAsync(BotSource, $"Channel with id [{channelId}] does not exist!"); - else await LogWarningAsync(BotSource, $"Channel is not a text channels and will not be used: {channel.Name} [{channel.Id}]!"); - continue; - } - - if (textChannel.Guild is RestGuild guild) { - await guild.UpdateAsync(); - await LogInfoAsync(BotSource, $"Whitelisted in channel: {channel.Name} [{channel.Id}] on server {guild.Name} [{guild.Id}]"); - } else { - await LogWarningAsync(BotSource, $"Whitelisted in channel: {channel.Name} [{channel.Id}] on unknown server!"); - } - yield return textChannel; + private async Task GetValidChannels(IEnumerable ids) + => (await Task.WhenAll(ids.Select(i => IsValidChannel(i)))).OfType().ToArray(); + private async Task IsValidChannel(ulong channelId) { + var channel = await _client.GetChannelAsync(channelId); + if (channel is not ITextChannel textChannel) { + if (channel is null) await LogWarningAsync(BotSource, $"Channel with id [{channelId}] does not exist!"); + else await LogWarningAsync(BotSource, $"Channel is not a text channels and will not be used: {channel.Name} [{channel.Id}]!"); + return null; } + + if (textChannel.Guild is RestGuild guild) { + await guild.UpdateAsync(); + await LogInfoAsync(BotSource, $"Whitelisted in channel: {channel.Name} [{channel.Id}] on server {guild.Name} [{guild.Id}]"); + } else { + await LogWarningAsync(BotSource, $"Whitelisted in channel: {channel.Name} [{channel.Id}] on unknown server!"); + } + return textChannel; } private async Task SocketReceived(IWebSocketConnection socket, string message) { @@ -276,11 +282,6 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana public static void LogError(string source, Exception exception) => Log(new(LogSeverity.Error, source, exception?.Message, exception)); private static async Task LogAsync(LogMessage msg) { - Log(msg); - await Task.CompletedTask; - } - - public static void Log(LogMessage msg) { lock (LogLock) { var oldColor = Console.ForegroundColor; try { @@ -298,8 +299,13 @@ public class Program : IDisposable, ICommandHandler, IUserRoleMana Console.ForegroundColor = oldColor; } } + if (msg.Severity <= DiscordLogSeverity && LogChannel is ITextChannel log) { + await log.SendMessageAsync($"{msg.Severity}: {msg}"); + } } + public static void Log(LogMessage msg) => _ = Task.Run(() => LogAsync(msg)); + protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) {