diff --git a/MinecraftClient/ChatBots/DiscordBridge.cs b/MinecraftClient/ChatBots/DiscordBridge.cs index 29c36ec4..77e9df99 100644 --- a/MinecraftClient/ChatBots/DiscordBridge.cs +++ b/MinecraftClient/ChatBots/DiscordBridge.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using DSharpPlus; @@ -11,6 +13,8 @@ namespace MinecraftClient.ChatBots { public class DiscordBridge : ChatBot { + private static DiscordBridge? instance = null; + private DiscordClient? _client; private DiscordChannel? _channel; @@ -44,6 +48,11 @@ namespace MinecraftClient.ChatBots public void OnSettingUpdate() { } } + public DiscordBridge() + { + instance = this; + } + public override void Initialize() { Task.Run(async () => await MainAsync()); @@ -74,6 +83,11 @@ namespace MinecraftClient.ChatBots } } + public static DiscordBridge? GetInstance() + { + return instance; + } + public override void GetText(string text) { if (_client == null || _channel == null) @@ -96,14 +110,6 @@ namespace MinecraftClient.ChatBots } else message = text; - SendMessageToDiscord(message, teleportRequest); - } - - private void SendMessageToDiscord(string message, bool teleportRequest = false) - { - if (_client == null || _channel == null) - return; - if (teleportRequest) { var messageBuilder = new DiscordMessageBuilder() @@ -117,13 +123,61 @@ namespace MinecraftClient.ChatBots new DiscordButtonComponent(ButtonStyle.Danger, "deny_teleport", "Deny") }); - _client.SendMessageAsync(_channel, messageBuilder).Wait(); + SendMessage(messageBuilder); return; } + else SendMessage(message); + } + + public void SendMessage(string message) + { + if (_client == null || _channel == null) + return; _client.SendMessageAsync(_channel, message).Wait(); } + public void SendMessage(DiscordMessageBuilder builder) + { + if (_client == null || _channel == null) + return; + + _client.SendMessageAsync(_channel, builder).Wait(); + } + + public void SendMessage(DiscordEmbedBuilder embedBuilder) + { + if (_client == null || _channel == null) + return; + + _client.SendMessageAsync(_channel, embedBuilder).Wait(); + + } + public void SendImage(string filePath, string? text = null) + { + if (_client == null || _channel == null) + return; + + using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + filePath = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..]; + var messageBuilder = new DiscordMessageBuilder().WithContent(text); + + messageBuilder.WithFiles(new Dictionary() { { $"attachment://{filePath}", fs } }); + + _client.SendMessageAsync(_channel, messageBuilder).Wait(); + } + } + + public void SendFile(FileStream fileStream) + { + if (_client == null || _channel == null) + return; + + var messageBuilder = new DiscordMessageBuilder().WithFile(fileStream); + SendMessage(messageBuilder); + } + async Task MainAsync() { try diff --git a/MinecraftClient/ChatBots/Map.cs b/MinecraftClient/ChatBots/Map.cs index 2b994c16..1c2b8786 100644 --- a/MinecraftClient/ChatBots/Map.cs +++ b/MinecraftClient/ChatBots/Map.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; +using ImageMagick; using MinecraftClient.Mapping; using Tomlet.Attributes; @@ -35,7 +36,20 @@ namespace MinecraftClient.ChatBots [TomlInlineComment("$config.ChatBot.Map.Notify_On_First_Update$")] public bool Notify_On_First_Update = true; - public void OnSettingUpdate() { } + [TomlInlineComment("$config.ChatBot.Map.Rasize_Rendered_Image$")] + public bool Rasize_Rendered_Image = false; + + [TomlInlineComment("$config.ChatBot.Map.Resize_To$")] + public int Resize_To = 512; + + [TomlPrecedingComment("$config.ChatBot.Map.Send_Rendered_To_Discord$")] + public bool Send_Rendered_To_Discord = false; + + public void OnSettingUpdate() + { + if (Resize_To <= 0) + Resize_To = 128; + } } private readonly string baseDirectory = @"Rendered_Maps"; @@ -47,10 +61,17 @@ namespace MinecraftClient.ChatBots if (!Directory.Exists(baseDirectory)) Directory.CreateDirectory(baseDirectory); + DeleteRenderedMaps(); + RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render or maps l|r ", OnMapCommand); } public override void OnUnload() + { + DeleteRenderedMaps(); + } + + private void DeleteRenderedMaps() { if (Config.Delete_All_On_Unload) { @@ -199,7 +220,48 @@ namespace MinecraftClient.ChatBots } } file.Close(); + LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName)); + + if (Config.Rasize_Rendered_Image) + { + using (var image = new MagickImage(fileName)) + { + var size = new MagickGeometry(Config.Resize_To, Config.Resize_To); + size.IgnoreAspectRatio = true; + + image.Resize(size); + image.Write(fileName); + LogToConsole(Translations.TryGet("bot.map.resized_rendered_image", map.MapId, Config.Resize_To)); + } + } + + if (Config.Send_Rendered_To_Discord) + { + if (DiscordBridge.Config.Enabled) + { + DiscordBridge? discordBridge = DiscordBridge.GetInstance(); + + if (discordBridge == null) + return; + + // We must convert to a PNG in order to send to Discord, BMP does not work + string newFileName = fileName.Replace(".bmp", ".png"); + using (var image = new MagickImage(fileName)) + { + image.Write(newFileName); + discordBridge.SendImage(newFileName, $"> A render of the map with an id: **{map.MapId}**"); + + newFileName = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + newFileName; + + // Delete the temporary file + if (File.Exists(newFileName)) + File.Delete(newFileName); + + LogToConsole(Translations.TryGet("bot.map.sent_to_discord", map.MapId)); + } + } + } } private void RenderInConsole(McMap map) @@ -389,8 +451,8 @@ namespace MinecraftClient.ChatBots return new( r: (byte)((Colors[baseColorId][0] * shadeMultiplier) / 255), - g: (byte)((Colors[baseColorId][1] * shadeMultiplier) / 255), - b: (byte)((Colors[baseColorId][2] * shadeMultiplier) / 255), + g: (byte)((Colors[baseColorId][1] * shadeMultiplier) / 255), + b: (byte)((Colors[baseColorId][2] * shadeMultiplier) / 255), a: 255 ); } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index 67e98941..4387bd2a 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -37,6 +37,7 @@ + diff --git a/MinecraftClient/Resources/lang/en.ini b/MinecraftClient/Resources/lang/en.ini index 6ed147ee..4d6a7068 100644 --- a/MinecraftClient/Resources/lang/en.ini +++ b/MinecraftClient/Resources/lang/en.ini @@ -711,6 +711,8 @@ bot.map.rendered=Succesfully rendered a map with id '{0}' to: '{1}' bot.map.failed_to_render=Failed to render the map with id: '{0}' bot.map.list_item=- Map id: {0} (Last Updated: {1}) bot.map.scale=The size of the map is reduced from ({0}x{1}) to ({2}x{3}) due to the size limitation of the current terminal. +bot.map.resized_rendered_image=Resized the rendered image of the map with id: '{0}' to {1}x{1}. +bot.map.sent_to_discord=Sent a rendered image of a map with an id '{0}' to the discord! # PlayerListLogger botname.PlayerListLogger=PlayerListLogger @@ -947,7 +949,7 @@ config.ChatBot.AutoRespond.Match_Colors=Do not remove colors from text (Note: Yo config.ChatBot.ChatLog=Logs chat messages in a file on disk. # ChatBot.DiscordBridge -config.ChatBot.DiscordBridge=This bot allows you to send and recieve messages and commands via a Discord channel.\n# For Setup you can either use the documentation or read here (Documentation has images).\n# Documentation: https://mccteam.github.io/guide/chat-bots.html#discord-bridge\n# Setup:\n# First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA .\n# IMPORTANT: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent" in order for bot to work! Also follow along carefully do not miss any steps!\n# When making a bot, copy the generated token and paste it here in "Token" field (tokens are important, keep them safe).\n# Copy the "Application ID" and go to: https://bit.ly/2Spn2Q3 .\n# Paste the id you have copied and check the "Administrator" field in permissions, then click on the link at the bottom.\n# This will open an invitation menu with your servers, choose the server you want to invite the bot on and invite him.\n# Once you've invited the bot, go to your Discord client and go to Settings -> Advanced and Enable "Developer Mode".\n# Exit the settings and right click on a server you have invited the bot to in the server list, then click "Copy ID", and paste the id here in "GuildId".\n# Then right click on a channel where you want to interact with the bot and again right click -> "Copy ID", pase the copied id here in "ChannelId".\n# And for the end, send a message in the channel, right click on your nick and again right click -> "Copy ID", then paste the id here in "OwnersIds".\n# How to use:\n# To execute an MCC command, prefix it with a dot ".", example: ".move 143 64 735" .\n# To send a message, simply type it out and hit enter. +config.ChatBot.DiscordBridge=This bot allows you to send and recieve messages and commands via a Discord channel.\n# For Setup you can either use the documentation or read here (Documentation has images).\n# Documentation: https://mccteam.github.io/guide/chat-bots.html#discord-bridge\n# Setup:\n# First you need to create a Bot on the Discord Developers Portal, here is a video tutorial: https://www.youtube.com/watch?v=2FgMnZViNPA .\n# /!\ IMPORTANT /!\: When creating a bot, you MUST ENABLE "Message Content Intent", "Server Members Intent" and "Presence Intent" in order for bot to work! Also follow along carefully do not miss any steps!\n# When making a bot, copy the generated token and paste it here in "Token" field (tokens are important, keep them safe).\n# Copy the "Application ID" and go to: https://bit.ly/2Spn2Q3 .\n# Paste the id you have copied and check the "Administrator" field in permissions, then click on the link at the bottom.\n# This will open an invitation menu with your servers, choose the server you want to invite the bot on and invite him.\n# Once you've invited the bot, go to your Discord client and go to Settings -> Advanced and Enable "Developer Mode".\n# Exit the settings and right click on a server you have invited the bot to in the server list, then click "Copy ID", and paste the id here in "GuildId".\n# Then right click on a channel where you want to interact with the bot and again right click -> "Copy ID", pase the copied id here in "ChannelId".\n# And for the end, send a message in the channel, right click on your nick and again right click -> "Copy ID", then paste the id here in "OwnersIds".\n# How to use:\n# To execute an MCC command, prefix it with a dot ".", example: ".move 143 64 735" .\n# To send a message, simply type it out and hit enter. config.ChatBot.DiscordBridge.Token=Your Discord Bot token. config.ChatBot.DiscordBridge.GuildId=The ID of a server/guild where you have invited the bot to. config.ChatBot.DiscordBridge.ChannelId=The ID of a channel where you want to interact with the MCC using the bot. @@ -970,12 +972,15 @@ config.ChatBot.HangmanGame=A small game to demonstrate chat interactions. Player config.ChatBot.Mailer=Relay messages between players and servers, like a mail plugin\n# This bot can store messages when the recipients are offline, and send them when they join the server\n# /!\ Server admins can spoof PMs (/tellraw, /nick) so enable this bot only if you trust server admins # ChatBot.Map -config.ChatBot.Map=Allows you to render maps into .jpg images\n# This is useful for solving captchas which use maps\n# The maps are rendered into Rendered_Maps folder.\n# NOTE:\n# This feature is currently only useful for solving captchas which use maps.\n# If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update and prepare to open the file quickly.\n# On linux you can use FTP to access generated files.\n# In the future it might will be possible to display maps directly in the console with a separate command.\n# /!\ Make sure server rules allow bots to be used on the server, or you risk being punished. +config.ChatBot.Map=Allows you to render maps in the console and into images (which can be then sent to Discord using Discord Bridge Chat Bot)\n# This is useful for solving captchas which use maps\n# The maps are rendered into Rendered_Maps folder if the Save_To_File is enabled.\n# NOTE:\n# If some servers have a very short time for solving captchas, enabe Auto_Render_On_Update to see them immediatelly in the console.\n# /!\ Make sure server rules allow bots to be used on the server, or you risk being punished. config.ChatBot.Map.Render_In_Console=Whether to render the map in the console. -config.ChatBot.Map.Save_To_File=Whether to store the rendered map as a file. +config.ChatBot.Map.Save_To_File=Whether to store the rendered map as a file (You need this setting if you want to get a map on Discord using Discord Bridge). config.ChatBot.Map.Auto_Render_On_Update=Automatically render the map once it is received or updated from/by the server -config.ChatBot.Map.Delete_All_On_Unload=Delete all rendered maps on unload/reload (Does not delete the images if you exit the client) +config.ChatBot.Map.Delete_All_On_Unload=Delete all rendered maps on unload/reload or when you launch the MCC again. config.ChatBot.Map.Notify_On_First_Update=Get a notification when you have gotten a map from the server for the first time +config.ChatBot.Map.Resize_To=The size that a rendered image should be resized to, in pixels (eg. 512). +config.ChatBot.Map.Rasize_Rendered_Image=Resize an rendered image, this is useful when images that are rendered are small and when are being sent to Discord. +config.ChatBot.Map.Send_Rendered_To_Discord=Send a rendered map (saved to a file) to a Discord channel via the Discord Bride chat bot (The Discord Bridge chat bot must be enabled and configured!)\n# You need to enable Save_To_File in order for this to work.\n# We also recommend turning on resizing. # ChatBot.PlayerListLogger config.ChatBot.PlayerListLogger=Log the list of players periodically into a textual file.