mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Merge branch 'master' into rpc
This commit is contained in:
commit
477da50fe0
24 changed files with 1230 additions and 116 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
using MinecraftClient.Inventory;
|
using MinecraftClient.Inventory;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
using Tomlet.Attributes;
|
using Tomlet.Attributes;
|
||||||
|
|
@ -143,7 +145,8 @@ namespace MinecraftClient.ChatBots
|
||||||
private Entity? fishingBobber;
|
private Entity? fishingBobber;
|
||||||
private Location LastPos = Location.Zero;
|
private Location LastPos = Location.Zero;
|
||||||
private DateTime CaughtTime = DateTime.Now;
|
private DateTime CaughtTime = DateTime.Now;
|
||||||
private int fishItemCounter = 10;
|
private int fishItemCounter = 15;
|
||||||
|
private Dictionary<ItemType, uint> fishItemCnt = new();
|
||||||
private Entity fishItem = new(-1, EntityType.Item, Location.Zero);
|
private Entity fishItem = new(-1, EntityType.Item, Location.Zero);
|
||||||
|
|
||||||
private int counter = 0;
|
private int counter = 0;
|
||||||
|
|
@ -182,7 +185,7 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public string CommandHandler(string cmd, string[] args)
|
public string CommandHandler(string cmd, string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length > 0)
|
if (args.Length >= 1)
|
||||||
{
|
{
|
||||||
switch (args[0])
|
switch (args[0])
|
||||||
{
|
{
|
||||||
|
|
@ -206,13 +209,47 @@ namespace MinecraftClient.ChatBots
|
||||||
}
|
}
|
||||||
StopFishing();
|
StopFishing();
|
||||||
return Translations.Get("bot.autoFish.stop");
|
return Translations.Get("bot.autoFish.stop");
|
||||||
|
case "status":
|
||||||
|
if (args.Length >= 2)
|
||||||
|
{
|
||||||
|
if (args[1] == "clear")
|
||||||
|
{
|
||||||
|
fishItemCnt = new();
|
||||||
|
return Translations.Get("bot.autoFish.status_clear");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetCommandHelp("status");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (fishItemCnt.Count == 0)
|
||||||
|
return Translations.Get("bot.autoFish.status_info");
|
||||||
|
|
||||||
|
List<KeyValuePair<ItemType, uint>> orderedList = fishItemCnt.OrderBy(x => x.Value).ToList();
|
||||||
|
int maxLen = orderedList[^1].Value.ToString().Length;
|
||||||
|
StringBuilder sb = new();
|
||||||
|
sb.Append(Translations.Get("bot.autoFish.status_info"));
|
||||||
|
foreach ((ItemType type, uint cnt) in orderedList)
|
||||||
|
{
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
|
||||||
|
string cntStr = cnt.ToString();
|
||||||
|
sb.Append(' ', maxLen - cntStr.Length).Append(cntStr);
|
||||||
|
sb.Append(" x ");
|
||||||
|
sb.Append(Item.GetTypeString(type));
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
case "help":
|
case "help":
|
||||||
return GetCommandHelp(args.Length >= 2 ? args[1] : "");
|
return GetCommandHelp(args.Length >= 2 ? args[1] : "");
|
||||||
default:
|
default:
|
||||||
return GetHelp();
|
return GetHelp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else return GetHelp();
|
else
|
||||||
|
return GetHelp();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartFishing()
|
private void StartFishing()
|
||||||
|
|
@ -246,7 +283,7 @@ namespace MinecraftClient.ChatBots
|
||||||
isFishing = false;
|
isFishing = false;
|
||||||
state = FishingState.Stopping;
|
state = FishingState.Stopping;
|
||||||
}
|
}
|
||||||
fishItemCounter = 10;
|
fishItemCounter = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UseFishRod()
|
private void UseFishRod()
|
||||||
|
|
@ -259,8 +296,9 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
if (fishItemCounter < 10)
|
if (fishItemCounter < 15)
|
||||||
++fishItemCounter;
|
++fishItemCounter;
|
||||||
|
|
||||||
lock (stateLock)
|
lock (stateLock)
|
||||||
{
|
{
|
||||||
switch (state)
|
switch (state)
|
||||||
|
|
@ -283,7 +321,7 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
if (castTimeout < 6000)
|
if (castTimeout < 6000)
|
||||||
castTimeout *= 2; // Exponential backoff
|
castTimeout *= 2; // Exponential backoff
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0));
|
LogToConsole(GetShortTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0));
|
||||||
|
|
||||||
counter = Settings.DoubleToTick(Config.Cast_Delay);
|
counter = Settings.DoubleToTick(Config.Cast_Delay);
|
||||||
state = FishingState.WaitingToCast;
|
state = FishingState.WaitingToCast;
|
||||||
|
|
@ -292,7 +330,7 @@ namespace MinecraftClient.ChatBots
|
||||||
case FishingState.WaitingFishToBite:
|
case FishingState.WaitingFishToBite:
|
||||||
if (++counter > Settings.DoubleToTick(Config.Fishing_Timeout))
|
if (++counter > Settings.DoubleToTick(Config.Fishing_Timeout))
|
||||||
{
|
{
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout"));
|
LogToConsole(GetShortTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout"));
|
||||||
|
|
||||||
counter = Settings.DoubleToTick(Config.Cast_Delay);
|
counter = Settings.DoubleToTick(Config.Cast_Delay);
|
||||||
state = FishingState.WaitingToCast;
|
state = FishingState.WaitingToCast;
|
||||||
|
|
@ -347,8 +385,8 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void OnEntitySpawn(Entity entity)
|
public override void OnEntitySpawn(Entity entity)
|
||||||
{
|
{
|
||||||
if (fishItemCounter < 10 && entity.Type == EntityType.Item && Math.Abs(entity.Location.Y - LastPos.Y) < 2.0 &&
|
if (fishItemCounter < 15 && entity.Type == EntityType.Item && Math.Abs(entity.Location.Y - LastPos.Y) < 2.2 &&
|
||||||
Math.Abs(entity.Location.X - LastPos.X) < 0.1 && Math.Abs(entity.Location.Z - LastPos.Z) < 0.1)
|
Math.Abs(entity.Location.X - LastPos.X) < 0.12 && Math.Abs(entity.Location.Z - LastPos.Z) < 0.12)
|
||||||
{
|
{
|
||||||
if (Config.Log_Fish_Bobber)
|
if (Config.Log_Fish_Bobber)
|
||||||
LogToConsole(string.Format("Item ({0}) spawn at {1}, distance = {2:0.00}", entity.ID, entity.Location, entity.Location.Distance(LastPos)));
|
LogToConsole(string.Format("Item ({0}) spawn at {1}, distance = {2:0.00}", entity.ID, entity.Location, entity.Location.Distance(LastPos)));
|
||||||
|
|
@ -359,9 +397,9 @@ namespace MinecraftClient.ChatBots
|
||||||
if (Config.Log_Fish_Bobber)
|
if (Config.Log_Fish_Bobber)
|
||||||
LogToConsole(string.Format("FishingBobber spawn at {0}, distance = {1:0.00}", entity.Location, GetCurrentLocation().Distance(entity.Location)));
|
LogToConsole(string.Format("FishingBobber spawn at {0}, distance = {1:0.00}", entity.Location, GetCurrentLocation().Distance(entity.Location)));
|
||||||
|
|
||||||
fishItemCounter = 10;
|
fishItemCounter = 15;
|
||||||
|
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.throw"));
|
LogToConsole(GetShortTimestamp() + ": " + Translations.Get("bot.autoFish.throw"));
|
||||||
lock (stateLock)
|
lock (stateLock)
|
||||||
{
|
{
|
||||||
fishingBobber = entity;
|
fishingBobber = entity;
|
||||||
|
|
@ -377,7 +415,7 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void OnEntityDespawn(Entity entity)
|
public override void OnEntityDespawn(Entity entity)
|
||||||
{
|
{
|
||||||
if (entity != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
|
if (entity != null && fishingBobber != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
|
||||||
{
|
{
|
||||||
if (Config.Log_Fish_Bobber)
|
if (Config.Log_Fish_Bobber)
|
||||||
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
|
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
|
||||||
|
|
@ -431,10 +469,15 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
public override void OnEntityMetadata(Entity entity, Dictionary<int, object?> metadata)
|
public override void OnEntityMetadata(Entity entity, Dictionary<int, object?> metadata)
|
||||||
{
|
{
|
||||||
if (fishItemCounter < 10 && entity.ID == fishItem.ID && metadata.TryGetValue(8, out object? item))
|
if (fishItemCounter < 15 && entity.ID == fishItem.ID && metadata.TryGetValue(8, out object? itemObj))
|
||||||
{
|
{
|
||||||
LogToConsole(Translations.Get("bot.autoFish.got", ((Item)item!).ToFullString()));
|
fishItemCounter = 15;
|
||||||
fishItemCounter = 10;
|
Item item = (Item)itemObj!;
|
||||||
|
LogToConsole(Translations.Get("bot.autoFish.got", item.ToFullString()));
|
||||||
|
if (fishItemCnt.ContainsKey(item.Type))
|
||||||
|
fishItemCnt[item.Type] += (uint)item.Count;
|
||||||
|
else
|
||||||
|
fishItemCnt.Add(item.Type, (uint)item.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,10 +514,10 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
++fishCount;
|
++fishCount;
|
||||||
if (Config.Enable_Move && Config.Movements.Length > 0)
|
if (Config.Enable_Move && Config.Movements.Length > 0)
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at",
|
LogToConsole(GetShortTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at",
|
||||||
fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount));
|
fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount));
|
||||||
else
|
else
|
||||||
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught", fishCount));
|
LogToConsole(GetShortTimestamp() + ": " + Translations.Get("bot.autoFish.caught", fishCount));
|
||||||
|
|
||||||
lock (stateLock)
|
lock (stateLock)
|
||||||
{
|
{
|
||||||
|
|
@ -580,7 +623,7 @@ namespace MinecraftClient.ChatBots
|
||||||
|
|
||||||
private static string GetHelp()
|
private static string GetHelp()
|
||||||
{
|
{
|
||||||
return Translations.Get("bot.autoFish.available_cmd", "start, stop, help");
|
return Translations.Get("bot.autoFish.available_cmd", "start, stop, status, help");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetCommandHelp(string cmd)
|
private string GetCommandHelp(string cmd)
|
||||||
|
|
@ -590,6 +633,7 @@ namespace MinecraftClient.ChatBots
|
||||||
#pragma warning disable format // @formatter:off
|
#pragma warning disable format // @formatter:off
|
||||||
"start" => Translations.Get("bot.autoFish.help.start"),
|
"start" => Translations.Get("bot.autoFish.help.start"),
|
||||||
"stop" => Translations.Get("bot.autoFish.help.stop"),
|
"stop" => Translations.Get("bot.autoFish.help.stop"),
|
||||||
|
"status" => Translations.Get("bot.autoFish.help.status"),
|
||||||
"help" => Translations.Get("bot.autoFish.help.help"),
|
"help" => Translations.Get("bot.autoFish.help.help"),
|
||||||
_ => GetHelp(),
|
_ => GetHelp(),
|
||||||
#pragma warning restore format // @formatter:on
|
#pragma warning restore format // @formatter:on
|
||||||
|
|
|
||||||
414
MinecraftClient/ChatBots/DiscordBridge.cs
Normal file
414
MinecraftClient/ChatBots/DiscordBridge.cs
Normal file
|
|
@ -0,0 +1,414 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DSharpPlus;
|
||||||
|
using DSharpPlus.Entities;
|
||||||
|
using DSharpPlus.Exceptions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Tomlet.Attributes;
|
||||||
|
|
||||||
|
namespace MinecraftClient.ChatBots
|
||||||
|
{
|
||||||
|
public class DiscordBridge : ChatBot
|
||||||
|
{
|
||||||
|
private enum BridgeDirection
|
||||||
|
{
|
||||||
|
Both = 0,
|
||||||
|
Minecraft,
|
||||||
|
Discord
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DiscordBridge? instance = null;
|
||||||
|
public bool IsConnected { get; private set; }
|
||||||
|
|
||||||
|
private DiscordClient? discordBotClient;
|
||||||
|
private DiscordChannel? discordChannel;
|
||||||
|
private BridgeDirection bridgeDirection = BridgeDirection.Both;
|
||||||
|
|
||||||
|
public static Configs Config = new();
|
||||||
|
|
||||||
|
[TomlDoNotInlineObject]
|
||||||
|
public class Configs
|
||||||
|
{
|
||||||
|
[NonSerialized]
|
||||||
|
private const string BotName = "DiscordBridge";
|
||||||
|
|
||||||
|
public bool Enabled = false;
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.DiscordBridge.Token$")]
|
||||||
|
public string Token = "your bot token here";
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.DiscordBridge.GuildId$")]
|
||||||
|
public ulong GuildId = 1018553894831403028L;
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.DiscordBridge.ChannelId$")]
|
||||||
|
public ulong ChannelId = 1018565295654326364L;
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.DiscordBridge.OwnersIds$")]
|
||||||
|
public ulong[] OwnersIds = new[] { 978757810781323276UL };
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.DiscordBridge.MessageSendTimeout$")]
|
||||||
|
public int Message_Send_Timeout = 3;
|
||||||
|
|
||||||
|
[TomlPrecedingComment("$config.ChatBot.DiscordBridge.Formats$")]
|
||||||
|
public string PrivateMessageFormat = "**[Private Message]** {username}: {message}";
|
||||||
|
public string PublicMessageFormat = "{username}: {message}";
|
||||||
|
public string TeleportRequestMessageFormat = "A new Teleport Request from **{username}**!";
|
||||||
|
|
||||||
|
public void OnSettingUpdate()
|
||||||
|
{
|
||||||
|
Message_Send_Timeout = Message_Send_Timeout <= 0 ? 3 : Message_Send_Timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DiscordBridge()
|
||||||
|
{
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
RegisterChatBotCommand("dscbridge", "bot.DiscordBridge.desc", "dscbridge direction <both|mc|discord>", OnDscCommand);
|
||||||
|
|
||||||
|
Task.Run(async () => await MainAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
~DiscordBridge()
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUnload()
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Disconnect()
|
||||||
|
{
|
||||||
|
if (discordBotClient != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (discordChannel != null)
|
||||||
|
discordBotClient.SendMessageAsync(discordChannel, new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Description = Translations.TryGet("bot.DiscordBridge.disconnected"),
|
||||||
|
Color = new DiscordColor(0xFF0000)
|
||||||
|
}).Wait(Config.Message_Send_Timeout * 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.DiscordBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
discordBotClient.DisconnectAsync().Wait();
|
||||||
|
IsConnected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DiscordBridge? GetInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string OnDscCommand(string cmd, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 2)
|
||||||
|
{
|
||||||
|
if (args[0].ToLower().Equals("direction"))
|
||||||
|
{
|
||||||
|
string direction = args[1].ToLower().Trim();
|
||||||
|
|
||||||
|
string? bridgeName = "";
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case "b":
|
||||||
|
case "both":
|
||||||
|
bridgeName = "bot.DiscordBridge.direction.both";
|
||||||
|
bridgeDirection = BridgeDirection.Both;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "mc":
|
||||||
|
case "minecraft":
|
||||||
|
bridgeName = "bot.DiscordBridge.direction.minecraft";
|
||||||
|
bridgeDirection = BridgeDirection.Minecraft;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "d":
|
||||||
|
case "dcs":
|
||||||
|
case "discord":
|
||||||
|
bridgeName = "bot.DiscordBridge.direction.discord";
|
||||||
|
bridgeDirection = BridgeDirection.Discord;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Translations.TryGet("bot.DiscordBridge.invalid_direction");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Translations.TryGet("bot.DiscordBridge.direction", Translations.TryGet(bridgeName));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return "dscbridge direction <both|mc|discord>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GetText(string text)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
text = GetVerbatim(text).Trim();
|
||||||
|
|
||||||
|
// Stop the crash when an empty text is recived somehow
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string message = "";
|
||||||
|
string username = "";
|
||||||
|
bool teleportRequest = false;
|
||||||
|
|
||||||
|
if (IsPrivateMessage(text, ref message, ref username))
|
||||||
|
message = Config.PrivateMessageFormat.Replace("{username}", username).Replace("{message}", message).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||||
|
else if (IsChatMessage(text, ref message, ref username))
|
||||||
|
message = Config.PublicMessageFormat.Replace("{username}", username).Replace("{message}", message).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||||
|
else if (IsTeleportRequest(text, ref username))
|
||||||
|
{
|
||||||
|
message = Config.TeleportRequestMessageFormat.Replace("{username}", username).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||||
|
teleportRequest = true;
|
||||||
|
}
|
||||||
|
else message = text;
|
||||||
|
|
||||||
|
if (teleportRequest)
|
||||||
|
{
|
||||||
|
var messageBuilder = new DiscordMessageBuilder()
|
||||||
|
.WithEmbed(new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Description = message,
|
||||||
|
Color = new DiscordColor(0x3399FF)
|
||||||
|
})
|
||||||
|
.AddComponents(new DiscordComponent[]{
|
||||||
|
new DiscordButtonComponent(ButtonStyle.Success, "accept_teleport", "Accept"),
|
||||||
|
new DiscordButtonComponent(ButtonStyle.Danger, "deny_teleport", "Deny")
|
||||||
|
});
|
||||||
|
|
||||||
|
SendMessage(messageBuilder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else SendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendMessage(string message)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages() || string.IsNullOrEmpty(message))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
discordBotClient!.SendMessageAsync(discordChannel, message).Wait(Config.Message_Send_Timeout * 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.DiscordBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendMessage(DiscordMessageBuilder builder)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
discordBotClient!.SendMessageAsync(discordChannel, builder).Wait(Config.Message_Send_Timeout * 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.DiscordBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendMessage(DiscordEmbedBuilder embedBuilder)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
discordBotClient!.SendMessageAsync(discordChannel, embedBuilder).Wait(Config.Message_Send_Timeout * 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.DiscordBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void SendImage(string filePath, string? text = null)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
|
||||||
|
{
|
||||||
|
filePath = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..];
|
||||||
|
var messageBuilder = new DiscordMessageBuilder();
|
||||||
|
|
||||||
|
if (text != null)
|
||||||
|
messageBuilder.WithContent(text);
|
||||||
|
|
||||||
|
messageBuilder.WithFiles(new Dictionary<string, Stream>() { { $"attachment://{filePath}", fs } });
|
||||||
|
|
||||||
|
discordBotClient!.SendMessageAsync(discordChannel, messageBuilder).Wait(Config.Message_Send_Timeout * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.DiscordBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendFile(FileStream fileStream)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
SendMessage(new DiscordMessageBuilder().WithFile(fileStream));
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanSendMessages()
|
||||||
|
{
|
||||||
|
return discordBotClient == null || discordChannel == null || bridgeDirection == BridgeDirection.Minecraft ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task MainAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Config.Token.Trim()))
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.TryGet("bot.DiscordBridge.missing_token"));
|
||||||
|
UnloadBot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
discordBotClient = new DiscordClient(new DiscordConfiguration()
|
||||||
|
{
|
||||||
|
Token = Config.Token.Trim(),
|
||||||
|
TokenType = TokenType.Bot,
|
||||||
|
AutoReconnect = true,
|
||||||
|
Intents = DiscordIntents.All,
|
||||||
|
MinimumLogLevel = Settings.Config.Logging.DebugMessages ?
|
||||||
|
(LogLevel.Trace | LogLevel.Information | LogLevel.Debug | LogLevel.Critical | LogLevel.Error | LogLevel.Warning) : LogLevel.None
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await discordBotClient.GetGuildAsync(Config.GuildId);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (e is NotFoundException)
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.TryGet("bot.DiscordBridge.guild_not_found", Config.GuildId));
|
||||||
|
UnloadBot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebugToConsole("Exception when trying to find the guild:");
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
discordChannel = await discordBotClient.GetChannelAsync(Config.ChannelId);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
if (e is NotFoundException)
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.TryGet("bot.DiscordBridge.channel_not_found", Config.ChannelId));
|
||||||
|
UnloadBot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebugToConsole("Exception when trying to find the channel:");
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
discordBotClient.MessageCreated += async (source, e) =>
|
||||||
|
{
|
||||||
|
if (e.Guild.Id != Config.GuildId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (e.Channel.Id != Config.ChannelId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Config.OwnersIds.Contains(e.Author.Id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string message = e.Message.Content.Trim();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(message) || string.IsNullOrWhiteSpace(message))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (bridgeDirection == BridgeDirection.Discord)
|
||||||
|
{
|
||||||
|
if (!message.StartsWith(".dscbridge"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.StartsWith("."))
|
||||||
|
{
|
||||||
|
message = message[1..];
|
||||||
|
await e.Message.CreateReactionAsync(DiscordEmoji.FromName(discordBotClient, ":gear:"));
|
||||||
|
|
||||||
|
string? result = "";
|
||||||
|
PerformInternalCommand(message, ref result);
|
||||||
|
result = string.IsNullOrEmpty(result) ? "-" : result;
|
||||||
|
|
||||||
|
await e.Message.DeleteOwnReactionAsync(DiscordEmoji.FromName(discordBotClient, ":gear:"));
|
||||||
|
await e.Message.CreateReactionAsync(DiscordEmoji.FromName(discordBotClient, ":white_check_mark:"));
|
||||||
|
await e.Message.RespondAsync($"{Translations.TryGet("bot.DiscordBridge.command_executed")}:\n```{result}```");
|
||||||
|
}
|
||||||
|
else SendText(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
discordBotClient.ComponentInteractionCreated += async (s, e) =>
|
||||||
|
{
|
||||||
|
if (!(e.Id.Equals("accept_teleport") || e.Id.Equals("deny_teleport")))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string result = e.Id.Equals("accept_teleport") ? "Accepted :white_check_mark:" : "Denied :x:";
|
||||||
|
SendText(e.Id.Equals("accept_teleport") ? "/tpaccept" : "/tpdeny");
|
||||||
|
await e.Interaction.CreateResponseAsync(InteractionResponseType.UpdateMessage, new DiscordInteractionResponseBuilder().WithContent(result));
|
||||||
|
};
|
||||||
|
|
||||||
|
await discordBotClient.ConnectAsync();
|
||||||
|
|
||||||
|
await discordBotClient.SendMessageAsync(discordChannel, new DiscordEmbedBuilder
|
||||||
|
{
|
||||||
|
Description = Translations.TryGet("bot.DiscordBridge.connected"),
|
||||||
|
Color = new DiscordColor(0x00FF00)
|
||||||
|
});
|
||||||
|
|
||||||
|
IsConnected = true;
|
||||||
|
LogToConsole("§y§l§f" + Translations.TryGet("bot.DiscordBridge.connected"));
|
||||||
|
await Task.Delay(-1);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.DiscordBridge.unknown_error"));
|
||||||
|
LogToConsole(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ImageMagick;
|
||||||
using MinecraftClient.Mapping;
|
using MinecraftClient.Mapping;
|
||||||
using Tomlet.Attributes;
|
using Tomlet.Attributes;
|
||||||
|
|
||||||
|
|
@ -12,6 +15,12 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
public static Configs Config = new();
|
public static Configs Config = new();
|
||||||
|
|
||||||
|
public struct QueuedMap
|
||||||
|
{
|
||||||
|
public string FileName;
|
||||||
|
public int MapId;
|
||||||
|
}
|
||||||
|
|
||||||
[TomlDoNotInlineObject]
|
[TomlDoNotInlineObject]
|
||||||
public class Configs
|
public class Configs
|
||||||
{
|
{
|
||||||
|
|
@ -35,22 +44,45 @@ namespace MinecraftClient.ChatBots
|
||||||
[TomlInlineComment("$config.ChatBot.Map.Notify_On_First_Update$")]
|
[TomlInlineComment("$config.ChatBot.Map.Notify_On_First_Update$")]
|
||||||
public bool Notify_On_First_Update = true;
|
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_Bridges$")]
|
||||||
|
public bool Send_Rendered_To_Discord = false;
|
||||||
|
public bool Send_Rendered_To_Telegram = false;
|
||||||
|
|
||||||
|
public void OnSettingUpdate()
|
||||||
|
{
|
||||||
|
if (Resize_To <= 0)
|
||||||
|
Resize_To = 128;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readonly string baseDirectory = @"Rendered_Maps";
|
private readonly string baseDirectory = @"Rendered_Maps";
|
||||||
|
|
||||||
private readonly Dictionary<int, McMap> cachedMaps = new();
|
private readonly Dictionary<int, McMap> cachedMaps = new();
|
||||||
|
|
||||||
|
private readonly Queue<QueuedMap> discordQueue = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(baseDirectory))
|
if (!Directory.Exists(baseDirectory))
|
||||||
Directory.CreateDirectory(baseDirectory);
|
Directory.CreateDirectory(baseDirectory);
|
||||||
|
|
||||||
|
DeleteRenderedMaps();
|
||||||
|
|
||||||
RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render <id> or maps l|r <id>", OnMapCommand);
|
RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render <id> or maps l|r <id>", OnMapCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnUnload()
|
public override void OnUnload()
|
||||||
|
{
|
||||||
|
DeleteRenderedMaps();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteRenderedMaps()
|
||||||
{
|
{
|
||||||
if (Config.Delete_All_On_Unload)
|
if (Config.Delete_All_On_Unload)
|
||||||
{
|
{
|
||||||
|
|
@ -199,7 +231,93 @@ namespace MinecraftClient.ChatBots
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file.Close();
|
file.Close();
|
||||||
|
|
||||||
LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName));
|
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 || Config.Send_Rendered_To_Telegram)
|
||||||
|
{
|
||||||
|
// We need to queue up images because Discord/Telegram Bridge is not ready immediatelly
|
||||||
|
if (DiscordBridge.Config.Enabled || TelegramBridge.Config.Enabled)
|
||||||
|
discordQueue.Enqueue(new QueuedMap { FileName = fileName, MapId = map.MapId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
DiscordBridge? discordBridge = DiscordBridge.GetInstance();
|
||||||
|
TelegramBridge? telegramBridge = TelegramBridge.GetInstance();
|
||||||
|
|
||||||
|
if (Config.Send_Rendered_To_Discord)
|
||||||
|
{
|
||||||
|
if (discordBridge == null || (discordBridge != null && !discordBridge.IsConnected))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.Send_Rendered_To_Telegram)
|
||||||
|
{
|
||||||
|
if (telegramBridge == null || (telegramBridge != null && !telegramBridge.IsConnected))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discordQueue.Count > 0)
|
||||||
|
{
|
||||||
|
QueuedMap map = discordQueue.Dequeue();
|
||||||
|
string fileName = map.FileName;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (Config.Send_Rendered_To_Discord)
|
||||||
|
discordBridge!.SendImage(newFileName, $"> A render of the map with an id: **{map.MapId}**");
|
||||||
|
|
||||||
|
if (Config.Send_Rendered_To_Telegram)
|
||||||
|
telegramBridge!.SendImage(newFileName, $"A render of the map with an id: *{map.MapId}*");
|
||||||
|
|
||||||
|
newFileName = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + newFileName;
|
||||||
|
|
||||||
|
if (Config.Send_Rendered_To_Discord)
|
||||||
|
LogToConsole(Translations.TryGet("bot.map.sent_to_discord", map.MapId));
|
||||||
|
|
||||||
|
if (Config.Send_Rendered_To_Telegram)
|
||||||
|
LogToConsole(Translations.TryGet("bot.map.sent_to_telegram", map.MapId));
|
||||||
|
|
||||||
|
// Wait for 2 seconds and then try until file is free for deletion
|
||||||
|
// 10 seconds timeout
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(2000);
|
||||||
|
|
||||||
|
var time = Stopwatch.StartNew();
|
||||||
|
|
||||||
|
while (time.ElapsedMilliseconds < 10000) // 10 seconds
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Delete the temporary file
|
||||||
|
if (File.Exists(newFileName))
|
||||||
|
File.Delete(newFileName);
|
||||||
|
}
|
||||||
|
catch (IOException e) { }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderInConsole(McMap map)
|
private void RenderInConsole(McMap map)
|
||||||
|
|
|
||||||
|
|
@ -158,7 +158,7 @@ namespace MinecraftClient.ChatBots
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CSharpRunner.Run(this, lines, args, localVars);
|
CSharpRunner.Run(this, lines, args, localVars, scriptName: file!);
|
||||||
}
|
}
|
||||||
catch (CSharpException e)
|
catch (CSharpException e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
358
MinecraftClient/ChatBots/TelegramBridge.cs
Normal file
358
MinecraftClient/ChatBots/TelegramBridge.cs
Normal file
|
|
@ -0,0 +1,358 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DSharpPlus.Entities;
|
||||||
|
using Telegram.Bot;
|
||||||
|
using Telegram.Bot.Exceptions;
|
||||||
|
using Telegram.Bot.Polling;
|
||||||
|
using Telegram.Bot.Types;
|
||||||
|
using Telegram.Bot.Types.Enums;
|
||||||
|
using Telegram.Bot.Types.InputFiles;
|
||||||
|
using Tomlet.Attributes;
|
||||||
|
using File = System.IO.File;
|
||||||
|
|
||||||
|
namespace MinecraftClient.ChatBots
|
||||||
|
{
|
||||||
|
public class TelegramBridge : ChatBot
|
||||||
|
{
|
||||||
|
private enum BridgeDirection
|
||||||
|
{
|
||||||
|
Both = 0,
|
||||||
|
Minecraft,
|
||||||
|
Telegram
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TelegramBridge? instance = null;
|
||||||
|
public bool IsConnected { get; private set; }
|
||||||
|
|
||||||
|
private TelegramBotClient? botClient;
|
||||||
|
private CancellationTokenSource? cancellationToken;
|
||||||
|
private BridgeDirection bridgeDirection = BridgeDirection.Both;
|
||||||
|
|
||||||
|
public static Configs Config = new();
|
||||||
|
|
||||||
|
[TomlDoNotInlineObject]
|
||||||
|
public class Configs
|
||||||
|
{
|
||||||
|
[NonSerialized]
|
||||||
|
private const string BotName = "TelegramBridge";
|
||||||
|
|
||||||
|
public bool Enabled = false;
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.TelegramBridge.Token$")]
|
||||||
|
public string Token = "your bot token here";
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.TelegramBridge.ChannelId$")]
|
||||||
|
public string ChannelId = "";
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.TelegramBridge.Authorized_Chat_Ids$")]
|
||||||
|
public long[] Authorized_Chat_Ids = Array.Empty<long>();
|
||||||
|
|
||||||
|
[TomlInlineComment("$config.ChatBot.TelegramBridge.MessageSendTimeout$")]
|
||||||
|
public int Message_Send_Timeout = 3;
|
||||||
|
|
||||||
|
[TomlPrecedingComment("$config.ChatBot.TelegramBridge.Formats$")]
|
||||||
|
public string PrivateMessageFormat = "*(Private Message)* {username}: {message}";
|
||||||
|
public string PublicMessageFormat = "{username}: {message}";
|
||||||
|
public string TeleportRequestMessageFormat = "A new Teleport Request from **{username}**!";
|
||||||
|
|
||||||
|
public void OnSettingUpdate()
|
||||||
|
{
|
||||||
|
Message_Send_Timeout = Message_Send_Timeout <= 0 ? 3 : Message_Send_Timeout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TelegramBridge()
|
||||||
|
{
|
||||||
|
instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
RegisterChatBotCommand("tgbridge", "bot.TelegramBridge.desc", "tgbridge direction <both|mc|telegram>", OnTgCommand);
|
||||||
|
|
||||||
|
Task.Run(async () => await MainAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
~TelegramBridge()
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnUnload()
|
||||||
|
{
|
||||||
|
Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Disconnect()
|
||||||
|
{
|
||||||
|
if (botClient != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SendMessage(Translations.TryGet("bot.TelegramBridge.disconnected"));
|
||||||
|
cancellationToken?.Cancel();
|
||||||
|
botClient = null;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.TelegramBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsConnected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TelegramBridge? GetInstance()
|
||||||
|
{
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string OnTgCommand(string cmd, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 2)
|
||||||
|
{
|
||||||
|
if (args[0].ToLower().Equals("direction"))
|
||||||
|
{
|
||||||
|
string direction = args[1].ToLower().Trim();
|
||||||
|
|
||||||
|
string? bridgeName = "";
|
||||||
|
|
||||||
|
switch (direction)
|
||||||
|
{
|
||||||
|
case "b":
|
||||||
|
case "both":
|
||||||
|
bridgeName = "bot.TelegramBridge.direction.both";
|
||||||
|
bridgeDirection = BridgeDirection.Both;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "mc":
|
||||||
|
case "minecraft":
|
||||||
|
bridgeName = "bot.TelegramBridge.direction.minecraft";
|
||||||
|
bridgeDirection = BridgeDirection.Minecraft;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "t":
|
||||||
|
case "tg":
|
||||||
|
case "telegram":
|
||||||
|
bridgeName = "bot.TelegramBridge.direction.discord";
|
||||||
|
bridgeDirection = BridgeDirection.Telegram;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Translations.TryGet("bot.TelegramBridge.invalid_direction");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Translations.TryGet("bot.TelegramBridge.direction", Translations.TryGet(bridgeName));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return "dscbridge direction <both|mc|discord>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void GetText(string text)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
text = GetVerbatim(text).Trim();
|
||||||
|
|
||||||
|
// Stop the crash when an empty text is recived somehow
|
||||||
|
if (string.IsNullOrEmpty(text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string message = "";
|
||||||
|
string username = "";
|
||||||
|
|
||||||
|
if (IsPrivateMessage(text, ref message, ref username))
|
||||||
|
message = Config.PrivateMessageFormat.Replace("{username}", username).Replace("{message}", message).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||||
|
else if (IsChatMessage(text, ref message, ref username))
|
||||||
|
message = Config.PublicMessageFormat.Replace("{username}", username).Replace("{message}", message).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||||
|
else if (IsTeleportRequest(text, ref username))
|
||||||
|
message = Config.TeleportRequestMessageFormat.Replace("{username}", username).Replace("{timestamp}", GetTimestamp()).Trim();
|
||||||
|
|
||||||
|
else message = text;
|
||||||
|
|
||||||
|
SendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendMessage(string message)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages() || string.IsNullOrEmpty(message))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
botClient!.SendTextMessageAsync(Config.ChannelId.Trim(), message, ParseMode.Markdown).Wait(Config.Message_Send_Timeout);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.TelegramBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendImage(string filePath, string? text = null)
|
||||||
|
{
|
||||||
|
if (!CanSendMessages())
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string fileName = filePath[(filePath.IndexOf(Path.DirectorySeparatorChar) + 1)..];
|
||||||
|
|
||||||
|
Stream stream = File.OpenRead(filePath);
|
||||||
|
botClient!.SendDocumentAsync(
|
||||||
|
Config.ChannelId.Trim(),
|
||||||
|
document: new InputOnlineFile(content: stream, fileName),
|
||||||
|
caption: text,
|
||||||
|
parseMode: ParseMode.Markdown).Wait(Config.Message_Send_Timeout * 1000);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.TelegramBridge.canceled_sending"));
|
||||||
|
LogDebugToConsole(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanSendMessages()
|
||||||
|
{
|
||||||
|
return botClient != null && !string.IsNullOrEmpty(Config.ChannelId.Trim()) && bridgeDirection != BridgeDirection.Minecraft;
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task MainAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Config.Token.Trim()))
|
||||||
|
{
|
||||||
|
LogToConsole(Translations.TryGet("bot.TelegramBridge.missing_token"));
|
||||||
|
UnloadBot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Config.ChannelId.Trim()))
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.TelegramBridge.missing_channel_id"));
|
||||||
|
|
||||||
|
botClient = new TelegramBotClient(Config.Token.Trim());
|
||||||
|
cancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
botClient.StartReceiving(
|
||||||
|
updateHandler: HandleUpdateAsync,
|
||||||
|
pollingErrorHandler: HandlePollingErrorAsync,
|
||||||
|
receiverOptions: new ReceiverOptions
|
||||||
|
{
|
||||||
|
// receive all update types
|
||||||
|
AllowedUpdates = Array.Empty<UpdateType>()
|
||||||
|
},
|
||||||
|
cancellationToken: cancellationToken.Token
|
||||||
|
);
|
||||||
|
|
||||||
|
IsConnected = true;
|
||||||
|
|
||||||
|
SendMessage("✅ " + Translations.TryGet("bot.TelegramBridge.connected"));
|
||||||
|
LogToConsole("§y§l§f" + Translations.TryGet("bot.TelegramBridge.connected"));
|
||||||
|
|
||||||
|
if (Config.Authorized_Chat_Ids.Length == 0)
|
||||||
|
{
|
||||||
|
SendMessage("⚠️ *" + Translations.TryGet("bot.TelegramBridge.missing_authorized_channels") + "* ⚠️");
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.TelegramBridge.missing_authorized_channels"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(-1);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogToConsole("§w§l§f" + Translations.TryGet("bot.TelegramBridge.unknown_error"));
|
||||||
|
LogToConsole(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update, CancellationToken _cancellationToken)
|
||||||
|
{
|
||||||
|
// Only process Message updates: https://core.telegram.org/bots/api#message
|
||||||
|
if (update.Message is not { } message)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Only process text messages
|
||||||
|
if (message.Text is not { } messageText)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var chatId = message.Chat.Id;
|
||||||
|
var text = message.Text;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(text) || string.IsNullOrWhiteSpace(text))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (text.ToLower().Contains("/start"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (text.ToLower().Contains(".chatid"))
|
||||||
|
{
|
||||||
|
await botClient.SendTextMessageAsync(chatId: chatId,
|
||||||
|
replyToMessageId: message.MessageId,
|
||||||
|
text: $"Chat ID: {chatId}",
|
||||||
|
cancellationToken: _cancellationToken,
|
||||||
|
parseMode: ParseMode.Markdown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Config.Authorized_Chat_Ids.Length > 0 && !Config.Authorized_Chat_Ids.Contains(chatId))
|
||||||
|
{
|
||||||
|
LogDebugToConsole($"Unauthorized message '{messageText}' received in a chat with with an ID: {chatId} !");
|
||||||
|
await botClient.SendTextMessageAsync(
|
||||||
|
chatId: chatId,
|
||||||
|
replyToMessageId: message.MessageId,
|
||||||
|
text: Translations.TryGet("bot.TelegramBridge.unauthorized"),
|
||||||
|
cancellationToken: _cancellationToken,
|
||||||
|
parseMode: ParseMode.Markdown);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogDebugToConsole($"Received a '{messageText}' message in a chat with with an ID: {chatId} .");
|
||||||
|
|
||||||
|
if (bridgeDirection == BridgeDirection.Telegram)
|
||||||
|
{
|
||||||
|
if (!text.StartsWith(".dscbridge"))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.StartsWith("."))
|
||||||
|
{
|
||||||
|
var command = text[1..];
|
||||||
|
|
||||||
|
string? result = "";
|
||||||
|
PerformInternalCommand(command, ref result);
|
||||||
|
result = string.IsNullOrEmpty(result) ? "-" : result;
|
||||||
|
|
||||||
|
await botClient.SendTextMessageAsync(
|
||||||
|
chatId: chatId,
|
||||||
|
replyToMessageId:
|
||||||
|
message.MessageId,
|
||||||
|
text: $"{Translations.TryGet("bot.TelegramBridge.command_executed")}:\n\n{result}",
|
||||||
|
cancellationToken: _cancellationToken,
|
||||||
|
parseMode: ParseMode.Markdown);
|
||||||
|
}
|
||||||
|
else SendText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task HandlePollingErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken _cancellationToken)
|
||||||
|
{
|
||||||
|
var ErrorMessage = exception switch
|
||||||
|
{
|
||||||
|
ApiRequestException apiRequestException
|
||||||
|
=> $"Telegram API Error:\n[{apiRequestException.ErrorCode}]\n{apiRequestException.Message}",
|
||||||
|
_ => exception.ToString()
|
||||||
|
};
|
||||||
|
|
||||||
|
LogToConsole("§w§l§f" + ErrorMessage);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -113,17 +113,22 @@ namespace MinecraftClient.Inventory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetTypeString()
|
public static string GetTypeString(ItemType type)
|
||||||
{
|
{
|
||||||
string type = Type.ToString();
|
string type_str = type.ToString();
|
||||||
string type_renamed = type.ToUnderscoreCase();
|
string type_renamed = type_str.ToUnderscoreCase();
|
||||||
string? res1 = Protocol.ChatParser.TranslateString("item.minecraft." + type_renamed);
|
string? res1 = Protocol.ChatParser.TranslateString("item.minecraft." + type_renamed);
|
||||||
if (!string.IsNullOrEmpty(res1))
|
if (!string.IsNullOrEmpty(res1))
|
||||||
return res1;
|
return res1;
|
||||||
string? res2 = Protocol.ChatParser.TranslateString("block.minecraft." + type_renamed);
|
string? res2 = Protocol.ChatParser.TranslateString("block.minecraft." + type_renamed);
|
||||||
if (!string.IsNullOrEmpty(res2))
|
if (!string.IsNullOrEmpty(res2))
|
||||||
return res2;
|
return res2;
|
||||||
return type;
|
return type_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetTypeString()
|
||||||
|
{
|
||||||
|
return GetTypeString(Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ToFullString()
|
public string ToFullString()
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,7 @@ namespace MinecraftClient
|
||||||
if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); }
|
if (Config.ChatBot.AutoRelog.Enabled) { BotLoad(new AutoRelog()); }
|
||||||
if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); }
|
if (Config.ChatBot.AutoRespond.Enabled) { BotLoad(new AutoRespond()); }
|
||||||
if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); }
|
if (Config.ChatBot.ChatLog.Enabled) { BotLoad(new ChatLog()); }
|
||||||
|
if (Config.ChatBot.DiscordBridge.Enabled) { BotLoad(new DiscordBridge()); }
|
||||||
if (Config.ChatBot.Farmer.Enabled) { BotLoad(new Farmer()); }
|
if (Config.ChatBot.Farmer.Enabled) { BotLoad(new Farmer()); }
|
||||||
if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); }
|
if (Config.ChatBot.FollowPlayer.Enabled) { BotLoad(new FollowPlayer()); }
|
||||||
if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); }
|
if (Config.ChatBot.HangmanGame.Enabled) { BotLoad(new HangmanGame()); }
|
||||||
|
|
@ -273,7 +274,9 @@ namespace MinecraftClient
|
||||||
if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); }
|
if (Config.ChatBot.RemoteControl.Enabled) { BotLoad(new RemoteControl()); }
|
||||||
if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); }
|
if (Config.ChatBot.ReplayCapture.Enabled && reload) { BotLoad(new ReplayCapture()); }
|
||||||
if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); }
|
if (Config.ChatBot.ScriptScheduler.Enabled) { BotLoad(new ScriptScheduler()); }
|
||||||
|
if (Config.ChatBot.TelegramBridge.Enabled) { BotLoad(new TelegramBridge()); }
|
||||||
if (Config.ChatBot.WebSocketBot.Enabled) { BotLoad(new WebSocketBot()); }
|
if (Config.ChatBot.WebSocketBot.Enabled) { BotLoad(new WebSocketBot()); }
|
||||||
|
|
||||||
//Add your ChatBot here by uncommenting and adapting
|
//Add your ChatBot here by uncommenting and adapting
|
||||||
//BotLoad(new ChatBots.YourBot());
|
//BotLoad(new ChatBots.YourBot());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DnsClient" Version="1.6.1" />
|
<PackageReference Include="DnsClient" Version="1.6.1" />
|
||||||
<PackageReference Include="DotNetZip" Version="1.16.0" />
|
<PackageReference Include="DotNetZip" Version="1.16.0" />
|
||||||
|
<PackageReference Include="DSharpPlus" Version="4.2.0" />
|
||||||
<PackageReference Include="DynamicExpresso.Core" Version="2.13.0" />
|
<PackageReference Include="DynamicExpresso.Core" Version="2.13.0" />
|
||||||
|
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="12.2.0" />
|
||||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
||||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
|
||||||
|
|
@ -46,6 +48,7 @@
|
||||||
<NoWarn>NU1701</NoWarn>
|
<NoWarn>NU1701</NoWarn>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Telegram.Bot" Version="18.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="config\ChatBots\AutoLook.cs" />
|
<Compile Remove="config\ChatBots\AutoLook.cs" />
|
||||||
|
|
|
||||||
|
|
@ -691,7 +691,7 @@ namespace MinecraftClient
|
||||||
public static void ReloadSettings()
|
public static void ReloadSettings()
|
||||||
{
|
{
|
||||||
if(Settings.LoadFromFile(settingsIniPath).Item1)
|
if(Settings.LoadFromFile(settingsIniPath).Item1)
|
||||||
ConsoleIO.WriteLine(Translations.TryGet("config.loading", settingsIniPath));
|
ConsoleIO.WriteLine(Translations.TryGet("config.load", settingsIniPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -477,20 +477,24 @@ cmd.useitem.use=Used an item
|
||||||
# ChatBots. Naming style: bot.<className>.<msg...>
|
# ChatBots. Naming style: bot.<className>.<msg...>
|
||||||
|
|
||||||
# Alerts
|
# Alerts
|
||||||
|
botname.Alerts=Alerts
|
||||||
bot.alerts.start_rain=§cWeather change: It is raining now.§r
|
bot.alerts.start_rain=§cWeather change: It is raining now.§r
|
||||||
bot.alerts.end_rain=§cWeather change: It is no longer raining.§r
|
bot.alerts.end_rain=§cWeather change: It is no longer raining.§r
|
||||||
bot.alerts.start_thunderstorm=§cWeather change: It is a thunderstorm.§r
|
bot.alerts.start_thunderstorm=§cWeather change: It is a thunderstorm.§r
|
||||||
bot.alerts.end_thunderstorm=§cWeather change: It is no longer a thunderstorm.§r
|
bot.alerts.end_thunderstorm=§cWeather change: It is no longer a thunderstorm.§r
|
||||||
|
|
||||||
# Anti AFK
|
# Anti AFK
|
||||||
|
botname.AntiAFK=AntiAFK
|
||||||
bot.antiafk.not_using_terrain_handling=The terrain handling is not enabled in the settings of the client, enable it if you want to use it with this bot. Using alternative (command) method.
|
bot.antiafk.not_using_terrain_handling=The terrain handling is not enabled in the settings of the client, enable it if you want to use it with this bot. Using alternative (command) method.
|
||||||
bot.antiafk.swapping=The time range begins with a bigger value, swapped them around.
|
bot.antiafk.swapping=The time range begins with a bigger value, swapped them around.
|
||||||
bot.antiafk.invalid_walk_range=Invalid walk range provided, must be a positive integer greater than 0, using default value of 5!
|
bot.antiafk.invalid_walk_range=Invalid walk range provided, must be a positive integer greater than 0, using default value of 5!
|
||||||
|
|
||||||
# AutoAttack
|
# AutoAttack
|
||||||
|
botname.AutoAttack=AutoAttack
|
||||||
bot.autoAttack.invalidcooldown=Attack cooldown value cannot be smaller than 0.
|
bot.autoAttack.invalidcooldown=Attack cooldown value cannot be smaller than 0.
|
||||||
|
|
||||||
# AutoCraft
|
# AutoCraft
|
||||||
|
botname.AutoCraft=AutoCraft
|
||||||
bot.autoCraft.cmd=Auto-crafting ChatBot command
|
bot.autoCraft.cmd=Auto-crafting ChatBot command
|
||||||
bot.autoCraft.alias=Auto-crafting ChatBot command alias
|
bot.autoCraft.alias=Auto-crafting ChatBot command alias
|
||||||
bot.autoCraft.cmd.list=Total {0} recipes loaded: {1}
|
bot.autoCraft.cmd.list=Total {0} recipes loaded: {1}
|
||||||
|
|
@ -523,6 +527,7 @@ bot.autocraft.invaild_slots=The number of slots does not match and has been adju
|
||||||
bot.autocraft.invaild_invaild_result=Invalid result item!
|
bot.autocraft.invaild_invaild_result=Invalid result item!
|
||||||
|
|
||||||
# AutoDig
|
# AutoDig
|
||||||
|
botname.AutoDig=AutoDig
|
||||||
bot.autodig.start_delay=Digging will start in {0:0.0} second(s).
|
bot.autodig.start_delay=Digging will start in {0:0.0} second(s).
|
||||||
bot.autodig.dig_timeout=Digging block timeout, retry.
|
bot.autodig.dig_timeout=Digging block timeout, retry.
|
||||||
bot.autodig.not_allow=The block currently pointed to is not in the allowed list.
|
bot.autodig.not_allow=The block currently pointed to is not in the allowed list.
|
||||||
|
|
@ -535,6 +540,7 @@ bot.autodig.help.stop=Deactivate the automatic digging bot.
|
||||||
bot.autodig.help.help=Get the command description. Usage: /digbot help <command name>
|
bot.autodig.help.help=Get the command description. Usage: /digbot help <command name>
|
||||||
|
|
||||||
# AutoDrop
|
# AutoDrop
|
||||||
|
botname.AutoDrop=AutoDrop
|
||||||
bot.autoDrop.cmd=AutoDrop ChatBot command
|
bot.autoDrop.cmd=AutoDrop ChatBot command
|
||||||
bot.autoDrop.alias=AutoDrop ChatBot command alias
|
bot.autoDrop.alias=AutoDrop ChatBot command alias
|
||||||
bot.autoDrop.on=AutoDrop enabled
|
bot.autoDrop.on=AutoDrop enabled
|
||||||
|
|
@ -550,7 +556,11 @@ bot.autoDrop.unknown_mode=Unknwon mode. Available modes: Include, Exclude, Every
|
||||||
bot.autoDrop.no_mode=Cannot read drop mode from config. Using include mode.
|
bot.autoDrop.no_mode=Cannot read drop mode from config. Using include mode.
|
||||||
bot.autoDrop.no_inventory=Cannot find inventory {0}!
|
bot.autoDrop.no_inventory=Cannot find inventory {0}!
|
||||||
|
|
||||||
|
# AutoEat
|
||||||
|
botname.AutoEat=AutoEat
|
||||||
|
|
||||||
# AutoFish
|
# AutoFish
|
||||||
|
botname.AutoFishing=AutoFishing
|
||||||
bot.autoFish.no_inv_handle=Inventory handling is not enabled. Cannot check rod durability and switch rods.
|
bot.autoFish.no_inv_handle=Inventory handling is not enabled. Cannot check rod durability and switch rods.
|
||||||
bot.autoFish.start_at=Fishing will start in {0:0.0} second(s).
|
bot.autoFish.start_at=Fishing will start in {0:0.0} second(s).
|
||||||
bot.autoFish.throw=Casting successfully.
|
bot.autoFish.throw=Casting successfully.
|
||||||
|
|
@ -563,15 +573,20 @@ bot.autoFish.fishing_timeout=Fishing timeout, will soon re-cast.
|
||||||
bot.autoFish.cast_timeout=Casting timeout and will soon retry. (Timeout increased to {0:0.0} sec).
|
bot.autoFish.cast_timeout=Casting timeout and will soon retry. (Timeout increased to {0:0.0} sec).
|
||||||
bot.autoFish.update_lookat=Update yaw = {0:0.00}, pitch = {1:0.00}.
|
bot.autoFish.update_lookat=Update yaw = {0:0.00}, pitch = {1:0.00}.
|
||||||
bot.autoFish.switch=Switch to the rod in slot {0}, durability {1}/64.
|
bot.autoFish.switch=Switch to the rod in slot {0}, durability {1}/64.
|
||||||
|
bot.autoFish.status_info=All items obtained from fishing (not entirely accurate):
|
||||||
|
# AutoFish cmd
|
||||||
bot.autoFish.cmd=Auto-Fishing ChatBot command
|
bot.autoFish.cmd=Auto-Fishing ChatBot command
|
||||||
bot.autoFish.available_cmd=Available commands: {0}. Use /fish help <cmd name> for more information.
|
bot.autoFish.available_cmd=Available commands: {0}. Use /fish help <cmd name> for more information.
|
||||||
bot.autoFish.start=Start auto-fishing.
|
bot.autoFish.start=Start auto-fishing.
|
||||||
bot.autoFish.stop=Stop auto-fishing.
|
bot.autoFish.stop=Stop auto-fishing.
|
||||||
|
bot.autoFish.status_clear=The record of the obtained items has been cleared.
|
||||||
bot.autoFish.help.start=Start auto-fishing.
|
bot.autoFish.help.start=Start auto-fishing.
|
||||||
bot.autoFish.help.stop=Stop auto-fishing.
|
bot.autoFish.help.stop=Stop auto-fishing.
|
||||||
|
bot.autoFish.help.status=List all obtained items. Or use "/fish status clear" to clear the list.
|
||||||
bot.autoFish.help.help=Get the command description. Usage: /fish help <command name>
|
bot.autoFish.help.help=Get the command description. Usage: /fish help <command name>
|
||||||
|
|
||||||
# AutoRelog
|
# AutoRelog
|
||||||
|
botname.AutoRelog=AutoRelog
|
||||||
bot.autoRelog.launch=Launching with {0} reconnection attempts
|
bot.autoRelog.launch=Launching with {0} reconnection attempts
|
||||||
bot.autoRelog.no_kick_msg=Initializing without a kick message file
|
bot.autoRelog.no_kick_msg=Initializing without a kick message file
|
||||||
bot.autoRelog.loading=Loading messages from file: {0}
|
bot.autoRelog.loading=Loading messages from file: {0}
|
||||||
|
|
@ -586,6 +601,7 @@ bot.autoRelog.reconnect_ignore=Message not containing any defined keywords. Igno
|
||||||
bot.autoRelog.wait=Waiting {0:0.000} seconds before reconnecting...
|
bot.autoRelog.wait=Waiting {0:0.000} seconds before reconnecting...
|
||||||
|
|
||||||
# AutoRespond
|
# AutoRespond
|
||||||
|
botname.AutoRespond=AutoRespond
|
||||||
bot.autoRespond.loading=Loading matches from '{0}'
|
bot.autoRespond.loading=Loading matches from '{0}'
|
||||||
bot.autoRespond.file_not_found=File not found: '{0}'
|
bot.autoRespond.file_not_found=File not found: '{0}'
|
||||||
bot.autoRespond.loaded_match=Loaded match:\n{0}
|
bot.autoRespond.loaded_match=Loaded match:\n{0}
|
||||||
|
|
@ -595,9 +611,70 @@ bot.autoRespond.match_run=Running action: {0}
|
||||||
bot.autoRespond.match=match: {0}\nregex: {1}\naction: {2}\nactionPrivate: {3}\nactionOther: {4}\nownersOnly: {5}\ncooldown: {6}
|
bot.autoRespond.match=match: {0}\nregex: {1}\naction: {2}\nactionPrivate: {3}\nactionOther: {4}\nownersOnly: {5}\ncooldown: {6}
|
||||||
|
|
||||||
# ChatLog
|
# ChatLog
|
||||||
|
botname.ChatLog=ChatLog
|
||||||
bot.chatLog.invalid_file=Path '{0}' contains invalid characters.
|
bot.chatLog.invalid_file=Path '{0}' contains invalid characters.
|
||||||
|
|
||||||
|
# DiscordBridge
|
||||||
|
botname.DiscordBridge=DiscordBridge
|
||||||
|
bot.DiscordBridge.command_executed=The command was executed with the result
|
||||||
|
bot.DiscordBridge.connected=Succesfully connected with MCC!
|
||||||
|
bot.DiscordBridge.missing_token=Please provide a valid token!
|
||||||
|
bot.DiscordBridge.guild_not_found=The provided guild/server with an id '{0}' has not been found!
|
||||||
|
bot.DiscordBridge.channel_not_found=The provided channel with an id '{0}' has not been found!
|
||||||
|
bot.DiscordBridge.unknown_error=An unknown error has occured!
|
||||||
|
bot.DiscordBridge.canceled_sending=Sending message to Discord was canceled due an error occuring. For more info enable Debug.
|
||||||
|
bot.DiscordBridge.desc=This command allows you to specify in the which direction the messages will be relayed via the Discord Bridge chat bot.
|
||||||
|
bot.DiscordBridge.invalid_direction=Invalid direction provided! Available directions: both|b, minecraft|mc, discord|dsc. Example: "dscbridge direction mc"
|
||||||
|
bot.DiscordBridge.direction=Direction of the Discord Brdige has been switched to '{0}'!
|
||||||
|
bot.DiscordBridge.direction.both=Both
|
||||||
|
bot.DiscordBridge.direction.minecraft=Minecraft
|
||||||
|
bot.DiscordBridge.direction.discord=Discord
|
||||||
|
|
||||||
|
# Farmer
|
||||||
|
botname.Farmer=Farmer
|
||||||
|
bot.farmer.desc=Farming bot
|
||||||
|
bot.farmer.not_implemented=Not implemented bellow 1.13!
|
||||||
|
bot.farmer.already_stopped=The bot has already stopped farming!
|
||||||
|
bot.farmer.stopping=Stoping farming, this might take a second...
|
||||||
|
bot.farmer.stopped=Stopped farming!
|
||||||
|
bot.farmer.already_running=The bot is already farming!
|
||||||
|
bot.farmer.invalid_crop_type=Invalid crop type provided (Types which you can use: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat)!
|
||||||
|
bot.farmer.warining_invalid_parameter=Invalid parameter "{0}" provided (Use format: "key:value")!
|
||||||
|
bot.farmer.invalid_radius=Invalid radius provided, you must provide a valid integer number greater than 0!
|
||||||
|
bot.farmer.warining_force_unsafe=You have enabled un-safe movement, the bot might get hurt!
|
||||||
|
bot.farmer.warining_allow_teleport=You have enabled teleporting, this might get your bot account kicked and in the worst case scenario banned! Use with caution!
|
||||||
|
bot.farmer.started=Started farming!
|
||||||
|
bot.farmer.crop_type=Crop type
|
||||||
|
bot.farmer.radius=Radius
|
||||||
|
bot.farmer.needs_terrain=The Farmer bot needs Terrain Handling in order to work, please enable it!
|
||||||
|
bot.farmer.needs_inventory=The Farmer bot needs Inventory Handling in order to work, please enable it!
|
||||||
|
|
||||||
|
# Follow player
|
||||||
|
botname.FollowPlayer=FollowPlayer
|
||||||
|
cmd.follow.desc=Makes the bot follow a specified player
|
||||||
|
cmd.follow.usage=follow <player name|stop> [-f] (Use -f to enable un-safe walking)
|
||||||
|
cmd.follow.already_stopped=Already stopped
|
||||||
|
cmd.follow.stopping=Stopped following!
|
||||||
|
cmd.follow.invalid_name=Invalid or empty player name provided!
|
||||||
|
cmd.follow.invalid_player=The specified player is either not connected out out of the range!
|
||||||
|
cmd.follow.cant_reach_player=Can not reach the player, he is either in chunks that are not loaded, too far away or not reachable by a bot due to obstacles like gaps or water bodies!
|
||||||
|
cmd.follow.already_following=Already following {0}!
|
||||||
|
cmd.follow.switched=Switched to following {0}!
|
||||||
|
cmd.follow.started=Started following {0}!
|
||||||
|
cmd.follow.unsafe_enabled=Enabled us-safe walking (NOTE: The bot might die or get hurt!)
|
||||||
|
cmd.follow.note=NOTE: The bot is quite slow, you need to walk slowly and at a close distance for it to be able to keep up, kinda like when you make animals follow you by holding food in your hand. This is a limitation due to a pathfinding algorithm, we are working to get a better one.
|
||||||
|
cmd.follow.player_came_to_the_range=The player {0} came back to the range!
|
||||||
|
cmd.follow.resuming=Resuming to follow!
|
||||||
|
cmd.follow.player_left_the_range=The player {0} has left the range!
|
||||||
|
cmd.follow.pausing=Pausing!
|
||||||
|
cmd.follow.player_left=The player {0} left the server!
|
||||||
|
cmd.follow.stopping=Stopped!
|
||||||
|
|
||||||
|
# HangmanGame
|
||||||
|
botname.HangmanGame=HangmanGame
|
||||||
|
|
||||||
# Mailer
|
# Mailer
|
||||||
|
botname.Mailer=Mailer
|
||||||
bot.mailer.init=Initializing Mailer with settings:
|
bot.mailer.init=Initializing Mailer with settings:
|
||||||
bot.mailer.init.db= - Database File: {0}
|
bot.mailer.init.db= - Database File: {0}
|
||||||
bot.mailer.init.ignore= - Ignore List: {0}
|
bot.mailer.init.ignore= - Ignore List: {0}
|
||||||
|
|
@ -630,6 +707,7 @@ bot.mailer.cmd.ignore.invalid=Missing or invalid name. Usage: {0} <username>
|
||||||
bot.mailer.cmd.help=See usage
|
bot.mailer.cmd.help=See usage
|
||||||
|
|
||||||
# Maps
|
# Maps
|
||||||
|
botname.Map=Map
|
||||||
bot.map.cmd.desc=Render maps (item maps)
|
bot.map.cmd.desc=Render maps (item maps)
|
||||||
bot.map.cmd.not_found=A map with id '{0}' does not exists!
|
bot.map.cmd.not_found=A map with id '{0}' does not exists!
|
||||||
bot.map.cmd.invalid_id=Invalid ID provided, must be a number!
|
bot.map.cmd.invalid_id=Invalid ID provided, must be a number!
|
||||||
|
|
@ -640,58 +718,32 @@ 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.failed_to_render=Failed to render the map with id: '{0}'
|
||||||
bot.map.list_item=- Map id: {0} (Last Updated: {1})
|
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.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 via Discord Brdige chat bot!
|
||||||
|
bot.map.sent_to_telegram=Sent a rendered image of a map with an id '{0}' to the Telegram via Telegram Bridge chat bot!
|
||||||
|
|
||||||
|
# PlayerListLogger
|
||||||
|
botname.PlayerListLogger=PlayerListLogger
|
||||||
|
|
||||||
|
# RemoteControl
|
||||||
|
botname.RemoteControl=RemoteControl
|
||||||
|
|
||||||
# ReplayCapture
|
# ReplayCapture
|
||||||
|
botname.ReplayCapture=ReplayCapture
|
||||||
bot.replayCapture.cmd=replay command
|
bot.replayCapture.cmd=replay command
|
||||||
bot.replayCapture.created=Replay file created.
|
bot.replayCapture.created=Replay file created.
|
||||||
bot.replayCapture.stopped=Record stopped.
|
bot.replayCapture.stopped=Record stopped.
|
||||||
bot.replayCapture.restart=Record was stopped. Restart the program to start another record.
|
bot.replayCapture.restart=Record was stopped. Restart the program to start another record.
|
||||||
|
|
||||||
# Farmer
|
|
||||||
bot.farmer.desc=Farming bot
|
|
||||||
bot.farmer.not_implemented=Not implemented bellow 1.13!
|
|
||||||
bot.farmer.already_stopped=The bot has already stopped farming!
|
|
||||||
bot.farmer.stopping=Stoping farming, this might take a second...
|
|
||||||
bot.farmer.stopped=Stopped farming!
|
|
||||||
bot.farmer.already_running=The bot is already farming!
|
|
||||||
bot.farmer.invalid_crop_type=Invalid crop type provided (Types which you can use: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat)!
|
|
||||||
bot.farmer.warining_invalid_parameter=Invalid parameter "{0}" provided (Use format: "key:value")!
|
|
||||||
bot.farmer.invalid_radius=Invalid radius provided, you must provide a valid integer number greater than 0!
|
|
||||||
bot.farmer.warining_force_unsafe=You have enabled un-safe movement, the bot might get hurt!
|
|
||||||
bot.farmer.warining_allow_teleport=You have enabled teleporting, this might get your bot account kicked and in the worst case scenario banned! Use with caution!
|
|
||||||
bot.farmer.started=Started farming!
|
|
||||||
bot.farmer.crop_type=Crop type
|
|
||||||
bot.farmer.radius=Radius
|
|
||||||
bot.farmer.needs_terrain=The Farmer bot needs Terrain Handling in order to work, please enable it!
|
|
||||||
bot.farmer.needs_inventory=The Farmer bot needs Inventory Handling in order to work, please enable it!
|
|
||||||
|
|
||||||
# Follow player
|
|
||||||
cmd.follow.desc=Makes the bot follow a specified player
|
|
||||||
cmd.follow.usage=follow <player name|stop> [-f] (Use -f to enable un-safe walking)
|
|
||||||
cmd.follow.already_stopped=Already stopped
|
|
||||||
cmd.follow.stopping=Stopped following!
|
|
||||||
cmd.follow.invalid_name=Invalid or empty player name provided!
|
|
||||||
cmd.follow.invalid_player=The specified player is either not connected out out of the range!
|
|
||||||
cmd.follow.cant_reach_player=Can not reach the player, he is either in chunks that are not loaded, too far away or not reachable by a bot due to obstacles like gaps or water bodies!
|
|
||||||
cmd.follow.already_following=Already following {0}!
|
|
||||||
cmd.follow.switched=Switched to following {0}!
|
|
||||||
cmd.follow.started=Started following {0}!
|
|
||||||
cmd.follow.unsafe_enabled=Enabled us-safe walking (NOTE: The bot might die or get hurt!)
|
|
||||||
cmd.follow.note=NOTE: The bot is quite slow, you need to walk slowly and at a close distance for it to be able to keep up, kinda like when you make animals follow you by holding food in your hand. This is a limitation due to a pathfinding algorithm, we are working to get a better one.
|
|
||||||
cmd.follow.player_came_to_the_range=The player {0} came back to the range!
|
|
||||||
cmd.follow.resuming=Resuming to follow!
|
|
||||||
cmd.follow.player_left_the_range=The player {0} has left the range!
|
|
||||||
cmd.follow.pausing=Pausing!
|
|
||||||
cmd.follow.player_left=The player {0} left the server!
|
|
||||||
cmd.follow.stopping=Stopped!
|
|
||||||
|
|
||||||
# Script
|
# Script
|
||||||
|
botname.Script=Script
|
||||||
bot.script.not_found=§8[MCC] [{0}] Cannot find script file: {1}
|
bot.script.not_found=§8[MCC] [{0}] Cannot find script file: {1}
|
||||||
bot.script.file_not_found=File not found: '{0}'
|
bot.script.file_not_found=File not found: '{0}'
|
||||||
bot.script.fail=Script '{0}' failed to run ({1}).
|
bot.script.fail=Script '{0}' failed to run ({1}).
|
||||||
bot.script.pm.loaded=Script '{0}' loaded.
|
bot.script.pm.loaded=Script '{0}' loaded.
|
||||||
|
|
||||||
# ScriptScheduler
|
# ScriptScheduler
|
||||||
|
botname.ScriptScheduler=ScriptScheduler
|
||||||
bot.scriptScheduler.loaded_task=Loaded task:\n{0}
|
bot.scriptScheduler.loaded_task=Loaded task:\n{0}
|
||||||
bot.scriptScheduler.no_trigger=This task will never trigger:\n{0}
|
bot.scriptScheduler.no_trigger=This task will never trigger:\n{0}
|
||||||
bot.scriptScheduler.no_action=No action for task:\n{0}
|
bot.scriptScheduler.no_action=No action for task:\n{0}
|
||||||
|
|
@ -700,6 +752,24 @@ bot.scriptScheduler.running_inverval=Interval / Running action: {0}
|
||||||
bot.scriptScheduler.running_login=Login / Running action: {0}
|
bot.scriptScheduler.running_login=Login / Running action: {0}
|
||||||
bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n triggerontime: {2}\n triggeroninterval: {3}\n timevalue: {4}\n timeinterval: {5}\n action: {6}
|
bot.scriptScheduler.task=triggeronfirstlogin: {0}\n triggeronlogin: {1}\n triggerontime: {2}\n triggeroninterval: {3}\n timevalue: {4}\n timeinterval: {5}\n action: {6}
|
||||||
|
|
||||||
|
# TelegramBridge
|
||||||
|
botname.TelegramBridge=TelegramBridge
|
||||||
|
bot.TelegramBridge.command_executed=The command was executed with the result
|
||||||
|
bot.TelegramBridge.connected=Succesfully connected with the MCC!
|
||||||
|
bot.TelegramBridge.disconnected=Disconnected from from the MCC!
|
||||||
|
bot.TelegramBridge.missing_token=Please provide a valid bot token!
|
||||||
|
bot.TelegramBridge.missing_channel_id=[WARNING] You have not provided a Channel ID, you will ONLY get replies to commands sent from Telegram!
|
||||||
|
bot.TelegramBridge.missing_authorized_channels=[WARNING] You have not provided any Channel IDs, for "Authorized_Chat_Ids" field, anyone who finds your bot will be able to send messages and commands to it!
|
||||||
|
bot.TelegramBridge.unauthorized=**🛑 Unauthorized access! 🛑\n\nAdd the ID of this chat to "Authorized_Chat_Ids" field in the configuration file to gain access!**
|
||||||
|
bot.TelegramBridge.unknown_error=An unknown error has occured!
|
||||||
|
bot.TelegramBridge.canceled_sending=Sending message to Telegram was canceled due an error occuring. For more info enable Debug.
|
||||||
|
bot.TelegramBridge.desc=This command allows you to specify in the which direction the messages will be relayed via the Telegram Bridge chat bot.
|
||||||
|
bot.TelegramBridge.invalid_direction=Invalid direction provided! Available directions: both|b, minecraft|mc, telegram|tg|t. Example: "tgbridge direction mc"
|
||||||
|
bot.TelegramBridge.direction=Direction of the Telegram Brdige has been switched to '{0}'!
|
||||||
|
bot.TelegramBridge.direction.both=Both
|
||||||
|
bot.TelegramBridge.direction.minecraft=Minecraft
|
||||||
|
bot.TelegramBridge.direction.Telegram=Telegram
|
||||||
|
|
||||||
# WebSocketBot
|
# WebSocketBot
|
||||||
bot.WebSocketBot.session_id_changed=§bSession with an id §a{0}§b has been renamed to: §a{1}§b!
|
bot.WebSocketBot.session_id_changed=§bSession with an id §a{0}§b has been renamed to: §a{1}§b!
|
||||||
bot.WebSocketBot.session_authenticated=§bSession with an id §a{0}§b has been succesfully authenticated!
|
bot.WebSocketBot.session_authenticated=§bSession with an id §a{0}§b has been succesfully authenticated!
|
||||||
|
|
@ -712,12 +782,12 @@ bot.WebSocketBot.new_session=§bNew session connected: §a{0}
|
||||||
bot.WebSocketBot.session_disconnected=§bSession with an id §a{0}§b has disconnected!
|
bot.WebSocketBot.session_disconnected=§bSession with an id §a{0}§b has disconnected!
|
||||||
|
|
||||||
# TestBot
|
# TestBot
|
||||||
|
botname.TestBot=TestBot
|
||||||
bot.testBot.told=Bot: {0} told me : {1}
|
bot.testBot.told=Bot: {0} told me : {1}
|
||||||
bot.testBot.said=Bot: {0} said : {1}
|
bot.testBot.said=Bot: {0} said : {1}
|
||||||
|
|
||||||
|
|
||||||
[config]
|
[config]
|
||||||
|
|
||||||
config.load=Settings have been loaded from {0}
|
config.load=Settings have been loaded from {0}
|
||||||
config.load.fail=§cFailed to load settings:§r
|
config.load.fail=§cFailed to load settings:§r
|
||||||
config.write.fail=§cFailed to write to settings file {0}§r
|
config.write.fail=§cFailed to write to settings file {0}§r
|
||||||
|
|
@ -915,6 +985,15 @@ config.ChatBot.AutoRespond.Match_Colors=Do not remove colors from text (Note: Yo
|
||||||
# ChatBot.ChatLog
|
# ChatBot.ChatLog
|
||||||
config.ChatBot.ChatLog=Logs chat messages in a file on disk.
|
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.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.
|
||||||
|
config.ChatBot.DiscordBridge.OwnersIds=A list of IDs of people you want to be able to interact with the MCC using the bot.
|
||||||
|
config.ChatBot.DiscordBridge.MessageSendTimeout=How long to wait (in seconds) if a message can not be sent to discord before canceling the task (minimum 1 second).
|
||||||
|
config.ChatBot.DiscordBridge.Formats=Message formats\n# Words wrapped with { and } are going to be replaced during the code execution, do not change them!\n# For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time.\n# For Discord message formatting, check the following: https://bit.ly/3F8CUCm
|
||||||
|
|
||||||
# ChatBot.Farmer
|
# ChatBot.Farmer
|
||||||
config.ChatBot.Farmer=Automatically farms crops for you (plants, breaks and bonemeals them).\n# Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat.\n# Usage: "/farmer start" command and "/farmer stop" command.\n# NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes.\n# or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this.\n# It is recommended to keep the farming area walled off and flat to avoid the bot jumping.\n# Also, if you have your farmland that is one block high, make it 2 or more blocks high so the bot does not fall through, as it can happen sometimes when the bot reconnects.\n# The bot also does not pickup all items if they fly off to the side, we have a plan to implement this option in the future as well as drop off and bonemeal refill chest(s).
|
config.ChatBot.Farmer=Automatically farms crops for you (plants, breaks and bonemeals them).\n# Crop types available: Beetroot, Carrot, Melon, Netherwart, Pumpkin, Potato, Wheat.\n# Usage: "/farmer start" command and "/farmer stop" command.\n# NOTE: This a newly added bot, it is not perfect and was only tested in 1.19.2, there are some minor issues like not being able to bonemeal carrots/potatoes sometimes.\n# or bot jumps onto the farm land and breaks it (this happens rarely but still happens). We are looking forward at improving this.\n# It is recommended to keep the farming area walled off and flat to avoid the bot jumping.\n# Also, if you have your farmland that is one block high, make it 2 or more blocks high so the bot does not fall through, as it can happen sometimes when the bot reconnects.\n# The bot also does not pickup all items if they fly off to the side, we have a plan to implement this option in the future as well as drop off and bonemeal refill chest(s).
|
||||||
config.ChatBot.Farmer.Delay_Between_Tasks=Delay between tasks in seconds (Minimum 1 second)
|
config.ChatBot.Farmer.Delay_Between_Tasks=Delay between tasks in seconds (Minimum 1 second)
|
||||||
|
|
@ -931,12 +1010,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
|
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
|
# 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.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.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.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_Bridges=Send a rendered map (saved to a file) to a Discord or a Telegram channel via the Discord or Telegram Bride chat bot (The Discord/Telegram 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
|
# ChatBot.PlayerListLogger
|
||||||
config.ChatBot.PlayerListLogger=Log the list of players periodically into a textual file.
|
config.ChatBot.PlayerListLogger=Log the list of players periodically into a textual file.
|
||||||
|
|
@ -952,6 +1034,14 @@ config.ChatBot.ReplayCapture.Backup_Interval=How long should replay file be auto
|
||||||
# ChatBot.ScriptScheduler
|
# ChatBot.ScriptScheduler
|
||||||
config.ChatBot.ScriptScheduler=Schedule commands and scripts to launch on various events such as server join, date/time or time interval\n# See https://mccteam.github.io/guide/chat-bots.html#script-scheduler for more info
|
config.ChatBot.ScriptScheduler=Schedule commands and scripts to launch on various events such as server join, date/time or time interval\n# See https://mccteam.github.io/guide/chat-bots.html#script-scheduler for more info
|
||||||
|
|
||||||
|
# ChatBot.TelegramBridge
|
||||||
|
config.ChatBot.TelegramBridge=This bot allows you to send and receive messages and commands via a Telegram Bot DM or to receive messages in a Telegram channel.\n# /!\ NOTE: You can't send messages and commands from a group channel, you can only send them in the bot DM, but you can get the messages from the client in a group channel.\n#-----------------------------------------------------------\n# Setup:\n# First you need to create a Telegram bot and obtain an API key, to do so, go to Telegram and find @botfather\n# Click on "Start" button and read the bot reply, then type "/newbot", the Botfather will guide you through the bot creation.\n# Once you create the bot, copy the API key that you have gotten, and put it into the "Token" field of "ChatBot.TelegramBridge" section (this section).\n# /!\ Do not share this token with anyone else as it will give them the control over your bot. Save it securely.\n# Then launch the client and go to Telegram, find your newly created bot by searching for it with its username, and open a DM with it.\n# Click on "Start" button and type and send the following command ".chatid" to obtain the chat id. \n# Copy the chat id number (eg. 2627844670) and paste it in the "ChannelId" field and add it to the "Authorized_Chat_Ids" field (in this section) (an id in "Authorized_Chat_Ids" field is a number/long, not a string!), then save the file.\n# Now you can use the bot using it's DM.\n# /!\ If you do not add the id of your chat DM with the bot to the "Authorized_Chat_Ids" field, ayone who finds your bot via search will be able to execute commands and send messages!\n# /!\ An id pasted in to the "Authorized_Chat_Ids" should be a number/long, not a string!\n#-----------------------------------------------------------\n# NOTE: If you want to recieve messages to a group channel instead, make the channel temporarely public, invite the bot to it and make it an administrator, then set the channel to private if you want.\n# Then set the "ChannelId" field to the @ of your channel (you must include the @ in the settings, eg. "@mysupersecretchannel"), this is the username you can see in the invite link of the channel.\n# /!\ Only include the username with @ prefix, do not include the rest of the link. Example if you have "https://t.me/mysupersecretchannel", the "ChannelId" will be "@mysupersecretchannel".\n# /!\ Note that you will not be able to send messages to the client from a group channel!\n#-----------------------------------------------------------\n# How to use the bot:\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.TelegramBridge.Token=Your Telegram Bot token.
|
||||||
|
config.ChatBot.TelegramBridge.ChannelId=An ID of a channel where you want to interact with the MCC using the bot.
|
||||||
|
config.ChatBot.TelegramBridge.Authorized_Chat_Ids=A list of Chat IDs that are allowed to send messages and execute commands. To get an id of your chat DM with the bot use ".chatid" bot command in Telegram.
|
||||||
|
config.ChatBot.TelegramBridge.MessageSendTimeout=How long to wait (in seconds) if a message can not be sent to Telegram before canceling the task (minimum 1 second).
|
||||||
|
config.ChatBot.TelegramBridge.Formats=Message formats\n# Words wrapped with { and } are going to be replaced during the code execution, do not change them!\n# For example. {message} is going to be replace with an actual message, {username} will be replaced with an username, {timestamp} with the current time.\n# For Telegram message formatting, check the following: https://sendpulse.com/blog/telegram-text-formatting
|
||||||
|
|
||||||
# ChatBot.WebSocketBot
|
# ChatBot.WebSocketBot
|
||||||
config.ChatBot.WebSocketBot=Remotely control the client using Web Sockets.\n# This is useful if you want to implement an application that can remotely and asynchronously execute procedures in MCC.\n# Example implementation written in JavaScript: https://github.com/milutinke/MCC.js.git\n# The protocol specification will be available in the documentation soon.
|
config.ChatBot.WebSocketBot=Remotely control the client using Web Sockets.\n# This is useful if you want to implement an application that can remotely and asynchronously execute procedures in MCC.\n# Example implementation written in JavaScript: https://github.com/milutinke/MCC.js.git\n# The protocol specification will be available in the documentation soon.
|
||||||
config.ChatBot.WebSocketBot.Ip=The IP address that Websocket server will be bounded to.
|
config.ChatBot.WebSocketBot.Ip=The IP address that Websocket server will be bounded to.
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
37
MinecraftClient/Scripting/AssemblyResolver.cs
Normal file
37
MinecraftClient/Scripting/AssemblyResolver.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace MinecraftClient.Scripting;
|
||||||
|
|
||||||
|
public static class AssemblyResolver {
|
||||||
|
private static Dictionary<string, string> ScriptAssemblies = new();
|
||||||
|
static AssemblyResolver() {
|
||||||
|
// Manually resolve assemblies that .NET can't resolve automatically.
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
|
||||||
|
{
|
||||||
|
var asmReqName = new AssemblyName(args.Name);
|
||||||
|
|
||||||
|
// Check the script-referenced assemblies if we have the DLL that is required.
|
||||||
|
foreach (var dll in ScriptAssemblies)
|
||||||
|
{
|
||||||
|
// If we have the assembly, load it.
|
||||||
|
if (asmReqName.FullName == dll.Key)
|
||||||
|
{
|
||||||
|
return Assembly.LoadFile(dll.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleIO.WriteLogLine($"[Script Error] Failed to resolve assembly {args.Name} (are you missing a DLL file?)");
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void AddAssembly(string AssemblyFullName, string AssemblyPath)
|
||||||
|
{
|
||||||
|
if (ScriptAssemblies.ContainsKey(AssemblyFullName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ScriptAssemblies.Add(AssemblyFullName, AssemblyPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,7 @@ namespace MinecraftClient
|
||||||
/// <param name="run">Set to false to compile and cache the script without launching it</param>
|
/// <param name="run">Set to false to compile and cache the script without launching it</param>
|
||||||
/// <exception cref="CSharpException">Thrown if an error occured</exception>
|
/// <exception cref="CSharpException">Thrown if an error occured</exception>
|
||||||
/// <returns>Result of the execution, returned by the script</returns>
|
/// <returns>Result of the execution, returned by the script</returns>
|
||||||
public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary<string, object>? localVars, bool run = true)
|
public static object? Run(ChatBot apiHandler, string[] lines, string[] args, Dictionary<string, object>? localVars, bool run = true, string scriptName = "Unknown Script")
|
||||||
{
|
{
|
||||||
//Script compatibility check for handling future versions differently
|
//Script compatibility check for handling future versions differently
|
||||||
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
|
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
|
||||||
|
|
@ -102,13 +102,24 @@ namespace MinecraftClient
|
||||||
"}}",
|
"}}",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ConsoleIO.WriteLogLine($"[Script] Starting compilation for {scriptName}...");
|
||||||
|
|
||||||
//Compile the C# class in memory using all the currently loaded assemblies
|
//Compile the C# class in memory using all the currently loaded assemblies
|
||||||
var result = compiler.Compile(code, Guid.NewGuid().ToString());
|
var result = compiler.Compile(code, Guid.NewGuid().ToString(), dlls);
|
||||||
|
|
||||||
//Process compile warnings and errors
|
//Process compile warnings and errors
|
||||||
if (result.Failures != null)
|
if (result.Failures != null) {
|
||||||
throw new CSharpException(CSErrorType.LoadError,
|
|
||||||
new InvalidOperationException(result.Failures[0].GetMessage()));
|
ConsoleIO.WriteLogLine("[Script] Compilation failed with error(s):");
|
||||||
|
|
||||||
|
foreach (var failure in result.Failures) {
|
||||||
|
ConsoleIO.WriteLogLine($"[Script] Error in {scriptName}, line:col{failure.Location.GetMappedLineSpan()}: [{failure.Id}] {failure.GetMessage()}");
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new CSharpException(CSErrorType.InvalidScript, new InvalidProgramException("Compilation failed due to error."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsoleIO.WriteLogLine("[Script] Compilation done with no errors.");
|
||||||
|
|
||||||
//Retrieve compiled assembly
|
//Retrieve compiled assembly
|
||||||
assembly = result.Assembly;
|
assembly = result.Assembly;
|
||||||
|
|
@ -385,7 +396,7 @@ namespace MinecraftClient
|
||||||
{
|
{
|
||||||
throw new CSharpException(CSErrorType.FileReadError, e);
|
throw new CSharpException(CSErrorType.FileReadError, e);
|
||||||
}
|
}
|
||||||
return CSharpRunner.Run(this, lines, args, localVars);
|
return CSharpRunner.Run(this, lines, args, localVars, scriptName: script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -835,10 +835,11 @@ namespace MinecraftClient
|
||||||
/// <param name="text">Log text to write</param>
|
/// <param name="text">Log text to write</param>
|
||||||
protected void LogToConsole(object? text)
|
protected void LogToConsole(object? text)
|
||||||
{
|
{
|
||||||
|
string botName = Translations.GetOrNull("botname." + GetType().Name) ?? GetType().Name;
|
||||||
if (_handler == null || master == null)
|
if (_handler == null || master == null)
|
||||||
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", GetType().Name, text));
|
ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", botName, text));
|
||||||
else
|
else
|
||||||
Handler.Log.Info(String.Format("[{0}] {1}", GetType().Name, text));
|
Handler.Log.Info(String.Format("[{0}] {1}", botName, text));
|
||||||
string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile);
|
string logfile = Settings.Config.AppVar.ExpandVars(Config.Main.Advanced.ChatbotLogFile);
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(logfile))
|
if (!String.IsNullOrEmpty(logfile))
|
||||||
|
|
@ -913,7 +914,10 @@ namespace MinecraftClient
|
||||||
protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0)
|
protected void ReconnectToTheServer(int ExtraAttempts = 3, int delaySeconds = 0)
|
||||||
{
|
{
|
||||||
if (Settings.Config.Logging.DebugMessages)
|
if (Settings.Config.Logging.DebugMessages)
|
||||||
ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", GetType().Name));
|
{
|
||||||
|
string botName = Translations.GetOrNull("botname." + GetType().Name) ?? GetType().Name;
|
||||||
|
ConsoleIO.WriteLogLine(Translations.Get("chatbot.reconnect", botName));
|
||||||
|
}
|
||||||
McClient.ReconnectionAttemptsLeft = ExtraAttempts;
|
McClient.ReconnectionAttemptsLeft = ExtraAttempts;
|
||||||
Program.Restart(delaySeconds);
|
Program.Restart(delaySeconds);
|
||||||
}
|
}
|
||||||
|
|
@ -1129,14 +1133,15 @@ namespace MinecraftClient
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected static string GetTimestamp()
|
protected static string GetTimestamp()
|
||||||
{
|
{
|
||||||
DateTime time = DateTime.Now;
|
return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
return String.Format("{0}-{1}-{2} {3}:{4}:{5}",
|
}
|
||||||
time.Year.ToString("0000"),
|
|
||||||
time.Month.ToString("00"),
|
/// <summary>
|
||||||
time.Day.ToString("00"),
|
/// Get a h:m:s timestamp representing the current system time
|
||||||
time.Hour.ToString("00"),
|
/// </summary>
|
||||||
time.Minute.ToString("00"),
|
protected static string GetShortTimestamp()
|
||||||
time.Second.ToString("00"));
|
{
|
||||||
|
return DateTime.Now.ToString("HH:mm:ss");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ namespace DynamicRun.Builder
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleIO.WriteLogLine(assemblyLoadContextWeakRef.Item1.IsAlive ? "Script continues to run." : "Script finished!");
|
ConsoleIO.WriteLogLine(assemblyLoadContextWeakRef.Item1.IsAlive ? "[Script] Script continues to run." : "[Script] Script finished!");
|
||||||
return assemblyLoadContextWeakRef.Item2;
|
return assemblyLoadContextWeakRef.Item2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,23 +15,20 @@ using Microsoft.CodeAnalysis;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
using Microsoft.CodeAnalysis.Text;
|
using Microsoft.CodeAnalysis.Text;
|
||||||
using MinecraftClient;
|
using MinecraftClient;
|
||||||
|
using MinecraftClient.Scripting;
|
||||||
using SingleFileExtractor.Core;
|
using SingleFileExtractor.Core;
|
||||||
|
|
||||||
namespace DynamicRun.Builder
|
namespace DynamicRun.Builder
|
||||||
{
|
{
|
||||||
internal class Compiler
|
internal class Compiler
|
||||||
{
|
{
|
||||||
public CompileResult Compile(string filepath, string fileName)
|
public CompileResult Compile(string filepath, string fileName, List<string> additionalAssemblies)
|
||||||
{
|
{
|
||||||
ConsoleIO.WriteLogLine($"Starting compilation...");
|
|
||||||
|
|
||||||
using var peStream = new MemoryStream();
|
using var peStream = new MemoryStream();
|
||||||
var result = GenerateCode(filepath, fileName).Emit(peStream);
|
var result = GenerateCode(filepath, fileName, additionalAssemblies).Emit(peStream);
|
||||||
|
|
||||||
if (!result.Success)
|
if (!result.Success)
|
||||||
{
|
{
|
||||||
ConsoleIO.WriteLogLine("Compilation done with error.");
|
|
||||||
|
|
||||||
var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
|
var failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error);
|
||||||
|
|
||||||
return new CompileResult()
|
return new CompileResult()
|
||||||
|
|
@ -42,8 +39,6 @@ namespace DynamicRun.Builder
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleIO.WriteLogLine("Compilation done without any error.");
|
|
||||||
|
|
||||||
peStream.Seek(0, SeekOrigin.Begin);
|
peStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
return new CompileResult()
|
return new CompileResult()
|
||||||
|
|
@ -54,15 +49,30 @@ namespace DynamicRun.Builder
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CSharpCompilation GenerateCode(string sourceCode, string fileName)
|
private static CSharpCompilation GenerateCode(string sourceCode, string fileName, List<string> additionalAssemblies)
|
||||||
{
|
{
|
||||||
var codeString = SourceText.From(sourceCode);
|
var codeString = SourceText.From(sourceCode);
|
||||||
var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);
|
var options = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);
|
||||||
|
|
||||||
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
|
var parsedSyntaxTree = SyntaxFactory.ParseSyntaxTree(codeString, options);
|
||||||
|
|
||||||
var mods = Assembly.GetEntryAssembly()!.GetModules();
|
var references = new List<MetadataReference>();
|
||||||
|
|
||||||
|
// Find if any additional assembly DLL exists in the base directory where the .exe exists.
|
||||||
|
foreach (var assembly in additionalAssemblies)
|
||||||
|
{
|
||||||
|
var dllPath = Path.Combine(AppContext.BaseDirectory, assembly);
|
||||||
|
if (File.Exists(dllPath))
|
||||||
|
{
|
||||||
|
references.Add(MetadataReference.CreateFromFile(dllPath));
|
||||||
|
// Store the reference in our Assembly Resolver for future reference.
|
||||||
|
AssemblyResolver.AddAssembly(Assembly.LoadFile(dllPath).FullName!, dllPath);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConsoleIO.WriteLogLine($"[Script Error] {assembly} is defined in script, but cannot find DLL! Script may not run.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma warning disable IL3000 // We determine if we are in a self-contained binary by checking specifically if the Assembly file path is null.
|
#pragma warning disable IL3000 // We determine if we are in a self-contained binary by checking specifically if the Assembly file path is null.
|
||||||
|
|
||||||
|
|
@ -70,8 +80,6 @@ namespace DynamicRun.Builder
|
||||||
var SystemConsole = typeof(Console).Assembly.Location; // System.Console
|
var SystemConsole = typeof(Console).Assembly.Location; // System.Console
|
||||||
var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll
|
var MinecraftClientDll = typeof(Program).Assembly.Location; // The path to MinecraftClient.dll
|
||||||
|
|
||||||
var references = new List<MetadataReference>();
|
|
||||||
|
|
||||||
// We're on a self-contained binary, so we need to extract the executable to get the assemblies.
|
// We're on a self-contained binary, so we need to extract the executable to get the assemblies.
|
||||||
if (string.IsNullOrEmpty(MinecraftClientDll))
|
if (string.IsNullOrEmpty(MinecraftClientDll))
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,7 @@ namespace MinecraftClient
|
||||||
{
|
{
|
||||||
string[] sip = General.Server.Host.Split(new[] { ":", ":" }, StringSplitOptions.None);
|
string[] sip = General.Server.Host.Split(new[] { ":", ":" }, StringSplitOptions.None);
|
||||||
General.Server.Host = sip[0];
|
General.Server.Host = sip[0];
|
||||||
|
InternalConfig.ServerIP = General.Server.Host;
|
||||||
|
|
||||||
if (sip.Length > 1)
|
if (sip.Length > 1)
|
||||||
{
|
{
|
||||||
|
|
@ -395,6 +396,9 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (General.Server.Port.HasValue)
|
||||||
|
InternalConfig.ServerPort = General.Server.Port.Value;
|
||||||
|
else
|
||||||
SetServerIP(General.Server, true);
|
SetServerIP(General.Server, true);
|
||||||
|
|
||||||
for (int i = 0; i < Advanced.BotOwners.Count; ++i)
|
for (int i = 0; i < Advanced.BotOwners.Count; ++i)
|
||||||
|
|
@ -1090,6 +1094,13 @@ namespace MinecraftClient
|
||||||
set { ChatBots.ChatLog.Config = value; ChatBots.ChatLog.Config.OnSettingUpdate(); }
|
set { ChatBots.ChatLog.Config = value; ChatBots.ChatLog.Config.OnSettingUpdate(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TomlPrecedingComment("$config.ChatBot.DiscordBridge$")]
|
||||||
|
public ChatBots.DiscordBridge.Configs DiscordBridge
|
||||||
|
{
|
||||||
|
get { return ChatBots.DiscordBridge.Config; }
|
||||||
|
set { ChatBots.DiscordBridge.Config = value; ChatBots.DiscordBridge.Config.OnSettingUpdate(); }
|
||||||
|
}
|
||||||
|
|
||||||
[TomlPrecedingComment("$config.ChatBot.Farmer$")]
|
[TomlPrecedingComment("$config.ChatBot.Farmer$")]
|
||||||
public ChatBots.Farmer.Configs Farmer
|
public ChatBots.Farmer.Configs Farmer
|
||||||
{
|
{
|
||||||
|
|
@ -1153,6 +1164,13 @@ namespace MinecraftClient
|
||||||
set { ChatBots.ScriptScheduler.Config = value; ChatBots.ScriptScheduler.Config.OnSettingUpdate(); }
|
set { ChatBots.ScriptScheduler.Config = value; ChatBots.ScriptScheduler.Config.OnSettingUpdate(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TomlPrecedingComment("$config.ChatBot.TelegramBridge$")]
|
||||||
|
public ChatBots.TelegramBridge.Configs TelegramBridge
|
||||||
|
{
|
||||||
|
get { return ChatBots.TelegramBridge.Config; }
|
||||||
|
set { ChatBots.TelegramBridge.Config = value; ChatBots.TelegramBridge.Config.OnSettingUpdate(); }
|
||||||
|
}
|
||||||
|
|
||||||
[TomlPrecedingComment("$config.ChatBot.WebSocketBot$")]
|
[TomlPrecedingComment("$config.ChatBot.WebSocketBot$")]
|
||||||
public ChatBots.WebSocketBot.Configs WebSocketBot
|
public ChatBots.WebSocketBot.Configs WebSocketBot
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -683,6 +683,7 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
StringBuilder sb = new();
|
StringBuilder sb = new();
|
||||||
int total = 0, translated = 0;
|
int total = 0, translated = 0;
|
||||||
|
int total_char = 0, translated_char = 0;
|
||||||
for (int i = 0; i < transEn.Length; ++i)
|
for (int i = 0; i < transEn.Length; ++i)
|
||||||
{
|
{
|
||||||
string line = transEn[i].Trim();
|
string line = transEn[i].Trim();
|
||||||
|
|
@ -693,23 +694,22 @@ namespace MinecraftClient
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
int en_value_len = line.Length - index;
|
||||||
string key = line[..index];
|
string key = line[..index];
|
||||||
sb.Append(key).Append('=');
|
sb.Append(key).Append('=');
|
||||||
if (trans.TryGetValue(key, out string? value))
|
if (trans.TryGetValue(key, out string? value))
|
||||||
{
|
{
|
||||||
sb.Append(value.Replace("\n", "\\n"));
|
sb.Append(value.Replace("\n", "\\n"));
|
||||||
++total;
|
|
||||||
++translated;
|
++translated;
|
||||||
|
translated_char += en_value_len;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
++total;
|
++total;
|
||||||
}
|
total_char += en_value_len;
|
||||||
}
|
}
|
||||||
sb.AppendLine();
|
sb.AppendLine();
|
||||||
}
|
}
|
||||||
File.WriteAllText(fileName, sb.ToString(), Encoding.Unicode);
|
File.WriteAllText(fileName, sb.ToString(), Encoding.Unicode);
|
||||||
ConsoleIO.WriteLine(string.Format("Language {0}: Translated {1} of {2}, {3:0.00}%", lang, translated, total, 100.0 * (double)translated / total));
|
ConsoleIO.WriteLine(string.Format("Language {0}: Translated {1} of {2}, {3:0.00}%", lang, translated, total, 100.0 * translated_char / total_char));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -52,13 +52,13 @@ If you'd like to contribute to Minecraft Console Client, great, just fork the re
|
||||||
Check out: [How to update or add translations for MCC](https://mccteam.github.io/guide/contibuting.html#translations).
|
Check out: [How to update or add translations for MCC](https://mccteam.github.io/guide/contibuting.html#translations).
|
||||||
|
|
||||||
MCC now supports the following languages (Alphabetical order) :
|
MCC now supports the following languages (Alphabetical order) :
|
||||||
* `de.ini` (51.34% translated) : Deutsch - German
|
* `de.ini` (30.21% translated) : Deutsch - German
|
||||||
* `en.ini` : English - English
|
* `en.ini` : English - English
|
||||||
* `fr.ini` (51.34% translated) : Français (France) - French
|
* `fr.ini` (30.21% translated) : Français (France) - French
|
||||||
* `ru.ini` (50.49% translated) : Русский (Russkiy) - Russian
|
* `ru.ini` (29.65% translated) : Русский (Russkiy) - Russian
|
||||||
* `vi.ini` (50.49% translated) : Tiếng Việt (Việt Nam) - Vietnamese
|
* `vi.ini` (29.65% translated) : Tiếng Việt (Việt Nam) - Vietnamese
|
||||||
* `zh-Hans.ini` (95.50% translated) : 简体中文 - Chinese Simplified
|
* `zh-Hans.ini` (87.08% translated) : 简体中文 - Chinese Simplified
|
||||||
* `zh-Hant.ini` (95.50% translated) : 繁體中文 - Chinese Traditional
|
* `zh-Hant.ini` (87.08% translated) : 繁體中文 - Chinese Traditional
|
||||||
|
|
||||||
## Building from the source 🏗️
|
## Building from the source 🏗️
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue