Refactoring Settings.cs

This commit is contained in:
BruceChen 2022-10-05 15:02:30 +08:00
parent f16b1c118b
commit 16c1d1fd77
59 changed files with 3425 additions and 2180 deletions

View file

@ -1,5 +1,6 @@
using System;
using System.Linq;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -8,9 +9,85 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class Alerts : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Alerts";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.Alerts.Beep_Enabled$")]
public bool Beep_Enabled = true;
[TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Words$")]
public bool Trigger_By_Words = false;
[TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Rain$")]
public bool Trigger_By_Rain = false;
[TomlInlineComment("$config.ChatBot.Alerts.Trigger_By_Thunderstorm$")]
public bool Trigger_By_Thunderstorm = false;
[TomlInlineComment("$config.ChatBot.Alerts.Matches_File$")]
public string Matches_File = @"alerts.txt";
[TomlInlineComment("$config.ChatBot.Alerts.Excludes_File$")]
public string Excludes_File = @"alerts-exclude.txt";
[TomlInlineComment("$config.ChatBot.Alerts.Log_To_File$")]
public bool Log_To_File = false;
[TomlInlineComment("$config.ChatBot.Alerts.Log_File$")]
public string Log_File = @"alerts-log.txt";
public void OnSettingUpdate()
{
if (!Enabled) return;
bool checkSuccessed = true;
if (Trigger_By_Words)
{
if (!System.IO.File.Exists(Matches_File))
{
checkSuccessed = false;
LogToConsole(BotName, "File not found: " + System.IO.Path.GetFullPath(Matches_File));
}
if (!System.IO.File.Exists(Excludes_File))
{
checkSuccessed = false;
LogToConsole(BotName, "File not found: " + System.IO.Path.GetFullPath(Excludes_File));
}
if (Log_To_File)
{
try
{
System.IO.File.AppendAllText(Log_File, string.Empty);
}
catch
{
checkSuccessed = false;
LogToConsole(BotName, "Can't write logs to " + System.IO.Path.GetFullPath(Excludes_File));
}
}
}
if (!checkSuccessed)
{
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
private string[] dictionary = Array.Empty<string>();
private string[] excludelist = Array.Empty<string>();
private bool logToFile = false;
float curRainLevel = 0;
float curThunderLevel = 0;
const float threshold = 0.2f;
@ -20,11 +97,10 @@ namespace MinecraftClient.ChatBots
/// </summary>
public override void Initialize()
{
if (Settings.Alerts_Trigger_By_Words)
if (Config.Trigger_By_Words)
{
dictionary = LoadDistinctEntriesFromFile(Settings.Alerts_MatchesFile);
excludelist = LoadDistinctEntriesFromFile(Settings.Alerts_ExcludesFile);
logToFile = Settings.Alerts_File_Logging;
dictionary = LoadDistinctEntriesFromFile(Config.Matches_File);
excludelist = LoadDistinctEntriesFromFile(Config.Excludes_File);
}
}
@ -34,7 +110,7 @@ namespace MinecraftClient.ChatBots
/// <param name="text">Received text</param>
public override void GetText(string text)
{
if (Settings.Alerts_Trigger_By_Words)
if (Config.Trigger_By_Words)
{
//Remove color codes and convert to lowercase
text = GetVerbatim(text).ToLower();
@ -45,16 +121,16 @@ namespace MinecraftClient.ChatBots
//Show an alert for each alert item found in text, if any
foreach (string alert in dictionary.Where(alert => text.Contains(alert)))
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
Console.Beep(); //Text found !
ConsoleIO.WriteLine(text.Replace(alert, "§c" + alert + "§r"));
if (logToFile && Settings.Alerts_LogFile.Length > 0)
if (Config.Log_To_File && Config.Log_File.Length > 0)
{
DateTime now = DateTime.Now;
string TimeStamp = "[" + now.Year + '/' + now.Month + '/' + now.Day + ' ' + now.Hour + ':' + now.Minute + ']';
System.IO.File.AppendAllText(Settings.Alerts_LogFile, TimeStamp + " " + GetVerbatim(text) + "\n");
System.IO.File.AppendAllText(Config.Log_File, TimeStamp + " " + GetVerbatim(text) + "\n");
}
}
}
@ -65,9 +141,9 @@ namespace MinecraftClient.ChatBots
{
if (curRainLevel < threshold && level >= threshold)
{
if (Settings.Alerts_Trigger_By_Rain)
if (Config.Trigger_By_Rain)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
Console.Beep();
@ -77,9 +153,9 @@ namespace MinecraftClient.ChatBots
}
else if (curRainLevel >= threshold && level < threshold)
{
if (Settings.Alerts_Trigger_By_Rain)
if (Config.Trigger_By_Rain)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
}
@ -93,9 +169,9 @@ namespace MinecraftClient.ChatBots
{
if (curThunderLevel < threshold && level >= threshold)
{
if (Settings.Alerts_Trigger_By_Thunderstorm)
if (Config.Trigger_By_Thunderstorm)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
Console.Beep();
@ -105,9 +181,9 @@ namespace MinecraftClient.ChatBots
}
else if (curThunderLevel >= threshold && level < threshold)
{
if (Settings.Alerts_Trigger_By_Thunderstorm)
if (Config.Trigger_By_Thunderstorm)
{
if (Settings.Alerts_Beep_Enabled)
if (Config.Beep_Enabled)
{
Console.Beep();
}

View file

@ -1,6 +1,6 @@
using System;
using System.Globalization;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -10,94 +10,94 @@ namespace MinecraftClient.ChatBots
public class AntiAFK : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AntiAFK";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AntiAfk.Delay$")]
public Range Delay = new(600);
[TomlInlineComment("$config.ChatBot.AntiAfk.Command$")]
public string Command = "/ping";
[TomlInlineComment("$config.ChatBot.AntiAfk.Use_Terrain_Handling$")]
public bool Use_Terrain_Handling = false;
[TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Range$")]
public int Walk_Range = 5;
[TomlInlineComment("$config.ChatBot.AntiAfk.Walk_Retries$")]
public int Walk_Retries = 20;
public void OnSettingUpdate()
{
if (Walk_Range <= 0)
{
Walk_Range = 5;
LogToConsole(BotName, Translations.TryGet("bot.antiafk.invalid_walk_range"));
}
if (Delay.min > Delay.max)
{
(Delay.min, Delay.max) = (Delay.max, Delay.min);
LogToConsole(BotName, Translations.TryGet("bot.antiafk.swapping"));
}
}
public struct Range
{
public int min, max;
public Range(int value)
{
value = Math.Max(value, 10);
min = max = value;
}
public Range(int min, int max)
{
min = Math.Max(min, 10);
max = Math.Max(max, 10);
this.min = min;
this.max = max;
}
}
}
private int count;
private readonly string pingparam;
private int timeping = 600;
private int timepingMax = -1;
private bool useTerrainHandling = false;
private bool previousSneakState = false;
private int walkRange = 5;
private readonly int walkRetries = 10;
private readonly Random random = new();
/// <summary>
/// This bot sends a /ping command every X seconds in order to stay non-afk.
/// </summary>
/// <param name="pingparam">Time amount between each ping (10 = 1s, 600 = 1 minute, etc.) Can be a range of numbers eg. 10-600</param>
public AntiAFK(string pingparam, bool useTerrainHandling, int walkRange, int walkRetries)
public AntiAFK()
{
count = 0;
this.pingparam = pingparam;
this.useTerrainHandling = useTerrainHandling;
this.walkRange = walkRange;
this.walkRetries = walkRetries;
}
public override void Initialize()
{
if (useTerrainHandling)
if (Config.Use_Terrain_Handling)
{
if (!GetTerrainEnabled())
{
useTerrainHandling = false;
LogToConsole(Translations.TryGet("bot.antiafk.not_using_terrain_handling"));
}
else
{
if (walkRange <= 0)
{
walkRange = 5;
LogToConsole(Translations.TryGet("bot.antiafk.invalid_walk_range"));
}
}
}
if (string.IsNullOrEmpty(pingparam))
LogToConsole(Translations.TryGet("bot.antiafk.invalid_time"));
else
{
// Handle the random range
if (pingparam.Contains('-'))
{
string[] parts = pingparam.Split("-");
if (parts.Length == 2)
{
if (int.TryParse(parts[0].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int firstTime))
{
timeping = firstTime;
if (int.TryParse(parts[1].Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int secondTime))
timepingMax = secondTime;
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range_partial", timeping));
}
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range"));
}
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_range"));
}
else
{
if (int.TryParse(pingparam.Trim(), NumberStyles.Any, CultureInfo.CurrentCulture, out int value))
timeping = value;
else LogToConsole(Translations.TryGet("bot.antiafk.invalid_value"));
}
}
if (timepingMax != -1 && timeping > timepingMax)
{
(timeping, timepingMax) = (timepingMax, timeping);
LogToConsole(Translations.TryGet("bot.antiafk.swapping"));
}
if (timeping < 10) { timeping = 10; } //To avoid flooding
}
public override void Update()
{
count++;
if ((timepingMax != -1 && count == random.Next(timeping, timepingMax)) || count == timeping)
if (count == random.Next(Config.Delay.min, Config.Delay.max))
{
DoAntiAfkStuff();
count = 0;
@ -107,7 +107,7 @@ namespace MinecraftClient.ChatBots
private void DoAntiAfkStuff()
{
if (useTerrainHandling)
if (Config.Use_Terrain_Handling && GetTerrainEnabled())
{
Location currentLocation = GetCurrentLocation();
Location goal;
@ -118,19 +118,19 @@ namespace MinecraftClient.ChatBots
while (!moved)
{
if (triesCounter++ >= walkRetries)
if (triesCounter++ >= Config.Walk_Retries)
{
useAlternativeMethod = true;
break;
}
goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange);
goal = GetRandomLocationWithinRangeXZ(currentLocation, Config.Walk_Range);
// Prevent getting the same location
while ((currentLocation.X == goal.X) && (currentLocation.Y == goal.Y) && (currentLocation.Z == goal.Z))
{
LogToConsole("Same location!, generating new one");
goal = GetRandomLocationWithinRangeXZ(currentLocation, walkRange);
goal = GetRandomLocationWithinRangeXZ(currentLocation, Config.Walk_Range);
}
if (!Movement.CheckChunkLoading(GetWorld(), currentLocation, goal))
@ -151,7 +151,7 @@ namespace MinecraftClient.ChatBots
}
}
SendText(Settings.AntiAFK_Command);
SendText(Config.Command);
Sneak(previousSneakState);
previousSneakState = !previousSneakState;
count = 0;

View file

@ -2,14 +2,89 @@
using System.Collections.Generic;
using System.IO;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
/// <summary>
/// The AutoAttack bot will automatically attack any hostile mob close to the player
/// </summary>
class AutoAttack : ChatBot
public class AutoAttack : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoAttack";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoAttack.Mode$")]
public AttackMode Mode = AttackMode.single;
[TomlInlineComment("$config.ChatBot.AutoAttack.Priority$")]
public PriorityType Priority = PriorityType.distance;
[TomlInlineComment("$config.ChatBot.AutoAttack.Cooldown_Time$")]
public CooldownConfig Cooldown_Time = new(false, 1.0);
[TomlInlineComment("$config.ChatBot.AutoAttack.Interaction$")]
public InteractType Interaction = InteractType.Attack;
[TomlInlineComment("$config.ChatBot.AutoAttack.Attack_Hostile$")]
public bool Attack_Hostile = true;
[TomlInlineComment("$config.ChatBot.AutoAttack.Attack_Passive$")]
public bool Attack_Passive = false;
[TomlInlineComment("$config.ChatBot.AutoAttack.List_Mode$")]
public ListType List_Mode = ListType.blacklist;
[TomlInlineComment("$config.ChatBot.AutoAttack.Entites_List$")]
public List<EntityType> Entites_List = new() { EntityType.Zombie, EntityType.Cow };
public void OnSettingUpdate()
{
if (Cooldown_Time.Custom && Cooldown_Time.value <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.autoAttack.invalidcooldown"));
Cooldown_Time.value = 1.0;
}
}
public enum AttackMode { single, multi };
public enum PriorityType { distance, health };
public enum ListType { blacklist, whitelist };
public struct CooldownConfig
{
public bool Custom;
public double value;
public CooldownConfig()
{
Custom = false;
value = 0;
}
public CooldownConfig(double value)
{
Custom = true;
this.value = value;
}
public CooldownConfig(bool Override, double value)
{
this.Custom = Override;
this.value = value;
}
}
}
private readonly Dictionary<int, Entity> entitiesToAttack = new(); // mobs within attack range
private int attackCooldown = 6;
private int attackCooldownCounter = 6;
@ -19,73 +94,20 @@ namespace MinecraftClient.ChatBots
private readonly int attackRange = 4;
private Double serverTPS;
private float health = 100;
private readonly bool singleMode = true;
private readonly bool priorityDistance = true;
private readonly InteractType interactMode;
private readonly bool attackHostile = true;
private readonly bool attackPassive = false;
private readonly string listMode = "blacklist";
private readonly List<EntityType> listedEntites = new();
public AutoAttack(
string mode, string priority, bool overrideAttackSpeed = false, double cooldownSeconds = 1, InteractType interaction = InteractType.Attack)
public AutoAttack()
{
if (mode == "single")
singleMode = true;
else if (mode == "multi")
singleMode = false;
else LogToConsoleTranslated("bot.autoAttack.mode", mode);
if (priority == "distance")
priorityDistance = true;
else if (priority == "health")
priorityDistance = false;
else LogToConsoleTranslated("bot.autoAttack.priority", priority);
interactMode = interaction;
if (overrideAttackSpeed)
overrideAttackSpeed = Config.Cooldown_Time.Custom;
if (Config.Cooldown_Time.Custom)
{
if (cooldownSeconds <= 0)
{
LogToConsoleTranslated("bot.autoAttack.invalidcooldown");
}
else
{
this.overrideAttackSpeed = overrideAttackSpeed;
attackCooldownSeconds = cooldownSeconds;
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
}
attackCooldownSeconds = Config.Cooldown_Time.value;
attackCooldown = Convert.ToInt32(Math.Truncate(attackCooldownSeconds / 0.1) + 1);
}
attackHostile = Settings.AutoAttack_Attack_Hostile;
attackPassive = Settings.AutoAttack_Attack_Passive;
if (Settings.AutoAttack_ListMode.Length > 0)
{
listMode = Settings.AutoAttack_ListMode.ToLower();
if (!(listMode.Equals("whitelist", StringComparison.OrdinalIgnoreCase) || listMode.Equals("blacklist", StringComparison.OrdinalIgnoreCase)))
{
LogToConsole(Translations.TryGet("bot.autoAttack.invalidlist"));
listMode = "blacklist";
}
}
else LogToConsole(Translations.TryGet("bot.autoAttack.invalidlist"));
if (File.Exists(Settings.AutoAttack_ListFile))
{
string[] entityList = LoadDistinctEntriesFromFile(Settings.AutoAttack_ListFile);
if (entityList.Length > 0)
{
foreach (var item in entityList)
{
if (Enum.TryParse(item, true, out EntityType resultingType))
listedEntites.Add(resultingType);
}
}
}
attackHostile = Config.Attack_Hostile;
attackPassive = Config.Attack_Passive;
}
public override void Initialize()
@ -108,10 +130,10 @@ namespace MinecraftClient.ChatBots
attackCooldownCounter = attackCooldown;
if (entitiesToAttack.Count > 0)
{
if (singleMode)
if (Config.Mode == Configs.AttackMode.single)
{
int priorityEntity = 0;
if (priorityDistance) // closest distance priority
if (Config.Priority == Configs.PriorityType.distance) // closest distance priority
{
double distance = 5;
foreach (var entity in entitiesToAttack)
@ -142,7 +164,7 @@ namespace MinecraftClient.ChatBots
// check entity distance and health again
if (ShouldAttackEntity(entitiesToAttack[priorityEntity]))
{
InteractEntity(priorityEntity, interactMode); // hit the entity!
InteractEntity(priorityEntity, Config.Interaction); // hit the entity!
SendAnimation(Inventory.Hand.MainHand); // Arm animation
}
}
@ -154,7 +176,7 @@ namespace MinecraftClient.ChatBots
// check that we are in range once again.
if (ShouldAttackEntity(entity.Value))
{
InteractEntity(entity.Key, interactMode); // hit the entity!
InteractEntity(entity.Key, Config.Interaction); // hit the entity!
}
}
SendAnimation(Inventory.Hand.MainHand); // Arm animation
@ -206,10 +228,13 @@ namespace MinecraftClient.ChatBots
if (attackPassive && entity.Type.IsPassive())
result = true;
if (listedEntites.Count > 0)
if (Config.Entites_List.Count > 0)
{
bool inList = listedEntites.Contains(entity.Type);
result = listMode.Equals("blacklist") ? (!inList && result) : (inList);
bool inList = Config.Entites_List.Contains(entity.Type);
if (Config.List_Mode == Configs.ListType.blacklist)
result = !inList && result;
else
result = inList;
}
return result;

View file

@ -4,11 +4,27 @@ using System.IO;
using System.Linq;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
class AutoCraft : ChatBot
public class AutoCraft : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoCraft";
public bool Enabled = false;
public string configFile = @"autocraft\config.ini";
public void OnSettingUpdate() { }
}
private bool waitingForMaterials = false;
private bool waitingForUpdate = false;
private bool waitingForTable = false;
@ -26,7 +42,6 @@ namespace MinecraftClient.ChatBots
private int updateTimeout = 0;
private string timeoutAction = "unspecified";
private readonly string configPath = @"autocraft\config.ini";
private string lastRecipe = ""; // Used in parsing recipe config
private readonly Dictionary<string, Recipe> recipes = new();
@ -158,11 +173,6 @@ namespace MinecraftClient.ChatBots
}
}
public AutoCraft(string configPath = @"autocraft\config.ini")
{
this.configPath = configPath;
}
public override void Initialize()
{
if (!GetInventoryEnabled())
@ -247,9 +257,9 @@ namespace MinecraftClient.ChatBots
public void LoadConfig()
{
if (!File.Exists(configPath))
if (!File.Exists(Config.configFile))
{
if (!Directory.Exists(configPath))
if (!Directory.Exists(Config.configFile))
{
Directory.CreateDirectory(@"autocraft");
}
@ -289,19 +299,19 @@ namespace MinecraftClient.ChatBots
"# For the naming of the items, please see",
"# https://github.com/MCCTeam/Minecraft-Console-Client/blob/master/MinecraftClient/Inventory/ItemType.cs"
};
File.WriteAllLines(configPath, content);
File.WriteAllLines(Config.configFile, content);
}
private void ParseConfig()
{
string[] content = File.ReadAllLines(configPath);
string[] content = File.ReadAllLines(Config.configFile);
if (content.Length <= 0)
{
throw new Exception(Translations.Get("bot.autoCraft.exception.empty", configPath));
throw new Exception(Translations.Get("bot.autoCraft.exception.empty", Config.configFile));
}
if (content[0].ToLower() != "[autocraft]")
{
throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", configPath));
throw new Exception(Translations.Get("bot.autoCraft.exception.invalid", Config.configFile));
}
// local variable for use in parsing config

View file

@ -2,51 +2,43 @@
using System.Collections.Generic;
using System.Linq;
using MinecraftClient.Inventory;
using Tomlet.Attributes;
using static MinecraftClient.ChatBots.AutoDrop.Configs;
namespace MinecraftClient.ChatBots
{
class AutoDrop : ChatBot
public class AutoDrop : ChatBot
{
private enum Mode
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
Include, // Items in list will be dropped
Exclude, // Items in list will be kept
Everything // Everything will be dropped
[NonSerialized]
private const string BotName = "AutoDrop";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoDrop.Mode$")]
public DropMode Mode = DropMode.include;
[TomlInlineComment("$config.ChatBot.AutoDrop.Items$")]
public List<ItemType> Items = new() { ItemType.Cobblestone, ItemType.Dirt };
public void OnSettingUpdate() { }
public enum DropMode
{
include, // Items in list will be dropped
exclude, // Items in list will be kept
everything // Everything will be dropped
}
}
private Mode dropMode = Mode.Include;
private bool enable = true;
private int updateDebounce = 0;
private readonly int updateDebounceValue = 2;
private int inventoryUpdated = -1;
private readonly List<ItemType> itemList = new();
public AutoDrop(string mode, string itemList)
{
if (!Enum.TryParse(mode, true, out dropMode))
{
LogToConsoleTranslated("bot.autoDrop.no_mode");
}
if (dropMode != Mode.Everything)
this.itemList = ItemListParser(itemList).ToList();
}
/// <summary>
/// Convert an item type string to item type array
/// </summary>
/// <param name="itemList">String to convert</param>
/// <returns>Item type array</returns>
private ItemType[] ItemListParser(string itemList)
{
string trimed = new(itemList.Where(c => !char.IsWhiteSpace(c)).ToArray());
List<ItemType> result = new();
foreach (string t in trimed.Split(','))
if (Enum.TryParse(t, true, out ItemType item))
result.Add(item);
return result.ToArray();
}
public string CommandHandler(string cmd, string[] args)
{
if (args.Length > 0)
@ -54,19 +46,19 @@ namespace MinecraftClient.ChatBots
switch (args[0].ToLower())
{
case "on":
enable = true;
Config.Enabled = true;
inventoryUpdated = 0;
OnUpdateFinish();
return Translations.Get("bot.autoDrop.on");
case "off":
enable = false;
Config.Enabled = false;
return Translations.Get("bot.autoDrop.off");
case "add":
if (args.Length >= 2)
{
if (Enum.TryParse(args[1], true, out ItemType item))
{
itemList.Add(item);
Config.Items.Add(item);
return Translations.Get("bot.autoDrop.added", item.ToString());
}
else
@ -83,9 +75,9 @@ namespace MinecraftClient.ChatBots
{
if (Enum.TryParse(args[1], true, out ItemType item))
{
if (itemList.Contains(item))
if (Config.Items.Contains(item))
{
itemList.Remove(item);
Config.Items.Remove(item);
return Translations.Get("bot.autoDrop.removed", item.ToString());
}
else
@ -103,9 +95,9 @@ namespace MinecraftClient.ChatBots
return Translations.Get("cmd.inventory.help.usage") + ": remove <item name>";
}
case "list":
if (itemList.Count > 0)
if (Config.Items.Count > 0)
{
return Translations.Get("bot.autoDrop.list", itemList.Count, string.Join("\n", itemList));
return Translations.Get("bot.autoDrop.list", Config.Items.Count, string.Join("\n", Config.Items));
}
else
{
@ -117,20 +109,20 @@ namespace MinecraftClient.ChatBots
switch (args[1].ToLower())
{
case "include":
dropMode = Mode.Include;
Config.Mode = DropMode.include;
break;
case "exclude":
dropMode = Mode.Exclude;
Config.Mode = DropMode.exclude;
break;
case "everything":
dropMode = Mode.Everything;
Config.Mode = DropMode.everything;
break;
default:
return Translations.Get("bot.autoDrop.unknown_mode"); // Unknwon mode. Available modes: Include, Exclude, Everything
}
inventoryUpdated = 0;
OnUpdateFinish();
return Translations.Get("bot.autoDrop.switched", dropMode.ToString()); // Switched to {0} mode.
return Translations.Get("bot.autoDrop.switched", Config.Mode.ToString()); // Switched to {0} mode.
}
else
{
@ -178,7 +170,7 @@ namespace MinecraftClient.ChatBots
public override void OnInventoryUpdate(int inventoryId)
{
if (enable)
if (Config.Enabled)
{
updateDebounce = updateDebounceValue;
// Always interact container if available (larger ID) because they included player inventory (ID 0)
@ -199,28 +191,28 @@ namespace MinecraftClient.ChatBots
}
var inventory = GetInventories()[inventoryUpdated];
var items = inventory.Items.ToDictionary(entry => entry.Key, entry => entry.Value);
if (dropMode == Mode.Include)
if (Config.Mode == DropMode.include)
{
foreach (var item in items)
{
// Ingore crafting result slot
if (item.Key == 0)
continue;
if (itemList.Contains(item.Value.Type))
if (Config.Items.Contains(item.Value.Type))
{
// Drop it !!
WindowAction(inventoryUpdated, item.Key, WindowActionType.DropItemStack);
}
}
}
else if (dropMode == Mode.Exclude)
else if (Config.Mode == DropMode.exclude)
{
foreach (var item in items)
{
// Ingore crafting result slot
if (item.Key == 0)
continue;
if (!itemList.Contains(item.Value.Type))
if (!Config.Items.Contains(item.Value.Type))
{
// Drop it !!
WindowAction(inventoryUpdated, item.Key, WindowActionType.DropItemStack);

View file

@ -1,19 +1,36 @@
using MinecraftClient.Inventory;
using System;
using MinecraftClient.Inventory;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
class AutoEat : ChatBot
public class AutoEat : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoEat";
public bool Enabled = false;
public int Threshold = 6;
public void OnSettingUpdate()
{
if (Threshold > 20)
Threshold = 20;
else if (Threshold < 0)
Threshold = 0;
}
}
byte LastSlot = 0;
public static bool Eating = false;
private readonly int HungerThreshold = 6;
private int DelayCounter = 0;
public AutoEat(int Threshold)
{
HungerThreshold = Threshold;
}
public override void Update()
{
if (DelayCounter > 0)
@ -31,7 +48,7 @@ namespace MinecraftClient.ChatBots
public override void OnHealthUpdate(float health, int food)
{
if (health <= 0) return; // player dead
if (((food <= HungerThreshold) || (food < 20 && health < 20)) && !Eating)
if (((food <= Config.Threshold) || (food < 20 && health < 20)) && !Eating)
{
Eating = FindFoodAndEat();
if (!Eating)

View file

@ -1,6 +1,9 @@
using System;
using MinecraftClient.Inventory;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
using static MinecraftClient.ChatBots.AutoFishing.Configs;
using static MinecraftClient.ChatBots.AutoFishing.Configs.LocationConfig;
namespace MinecraftClient.ChatBots
{
@ -8,8 +11,122 @@ namespace MinecraftClient.ChatBots
/// The AutoFishing bot semi-automates fishing.
/// The player needs to have a fishing rod in hand, then manually send it using the UseItem command.
/// </summary>
class AutoFishing : ChatBot
public class AutoFishing : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoFishing";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoFishing.Antidespawn$")]
public bool Antidespawn = false;
[TomlInlineComment("$config.ChatBot.AutoFishing.Mainhand$")]
public bool Mainhand = true;
[TomlInlineComment("$config.ChatBot.AutoFishing.Auto_Start$")]
public bool Auto_Start = true;
[TomlInlineComment("$config.ChatBot.AutoFishing.Cast_Delay$")]
public double Cast_Delay = 0.4;
[TomlInlineComment("$config.ChatBot.AutoFishing.Fishing_Delay$")]
public double Fishing_Delay = 3.0;
[TomlInlineComment("$config.ChatBot.AutoFishing.Fishing_Timeout$")]
public double Fishing_Timeout = 300.0;
[TomlInlineComment("$config.ChatBot.AutoFishing.Durability_Limit$")]
public double Durability_Limit = 2;
[TomlInlineComment("$config.ChatBot.AutoFishing.Auto_Rod_Switch$")]
public bool Auto_Rod_Switch = true;
[TomlInlineComment("$config.ChatBot.AutoFishing.Stationary_Threshold$")]
public double Stationary_Threshold = 0.001;
[TomlInlineComment("$config.ChatBot.AutoFishing.Hook_Threshold$")]
public double Hook_Threshold = 0.2;
[TomlInlineComment("$config.ChatBot.AutoFishing.Log_Fish_Bobber$")]
public bool Log_Fish_Bobber = false;
[TomlInlineComment("$config.ChatBot.AutoFishing.Movements$")]
public LocationConfig[] Movements = Array.Empty<LocationConfig>();
public void OnSettingUpdate()
{
if (Cast_Delay < 0)
Cast_Delay = 0;
if (Fishing_Delay < 0)
Fishing_Delay = 0;
if (Fishing_Timeout < 0)
Fishing_Timeout = 0;
if (Durability_Limit < 0)
Durability_Limit = 0;
else if (Durability_Limit > 64)
Durability_Limit = 64;
if (Stationary_Threshold < 0)
Stationary_Threshold = -Stationary_Threshold;
if (Hook_Threshold < 0)
Hook_Threshold = -Hook_Threshold;
}
public struct LocationConfig
{
public Coordination? XYZ;
public Facing? facing;
public LocationConfig(float yaw, float pitch)
{
this.XYZ = null;
this.facing = new(yaw, pitch);
}
public LocationConfig(double x, double y, double z)
{
this.XYZ = new(x, y, z);
this.facing = null;
}
public LocationConfig(double x, double y, double z, float yaw, float pitch)
{
this.XYZ = new(x, y, z);
this.facing = new(yaw, pitch);
}
public struct Coordination
{
public double x, y, z;
public Coordination(double x, double y, double z)
{
this.x = x; this.y = y; this.z = z;
}
}
public struct Facing
{
public float yaw, pitch;
public Facing(float yaw, float pitch)
{
this.yaw = yaw; this.pitch = pitch;
}
}
}
}
private int fishCount = 0;
private bool inventoryEnabled;
private int castTimeout = 12;
@ -54,9 +171,9 @@ namespace MinecraftClient.ChatBots
private void StartFishing()
{
isFishing = false;
if (Settings.AutoFishing_AutoStart)
if (Config.Auto_Start)
{
double delay = Settings.AutoFishing_FishingDelay;
double delay = Config.Fishing_Delay;
LogToConsole(Translations.Get("bot.autoFish.start", delay));
lock (stateLock)
{
@ -84,7 +201,7 @@ namespace MinecraftClient.ChatBots
private void UseFishRod()
{
if (Settings.AutoFishing_Mainhand)
if (Config.Mainhand)
UseItemInHand();
else
UseItemInLeftHand();
@ -100,7 +217,7 @@ namespace MinecraftClient.ChatBots
break;
case FishingState.WaitingToCast:
if (AutoEat.Eating)
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
else if (--counter < 0)
state = FishingState.CastingRod;
break;
@ -116,28 +233,27 @@ namespace MinecraftClient.ChatBots
castTimeout *= 2; // Exponential backoff
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.cast_timeout", castTimeout / 10.0));
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
break;
case FishingState.WaitingFishToBite:
if (++counter > (int)(Settings.AutoFishing_FishingTimeout * 10))
if (++counter > (int)(Config.Fishing_Timeout * 10))
{
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.fishing_timeout"));
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
break;
case FishingState.StartMove:
if (--counter < 0)
{
double[,]? locationList = Settings.AutoFishing_Location;
if (locationList != null)
if (Config.Movements.Length > 0)
{
if (GetTerrainEnabled())
{
UpdateLocation(locationList);
UpdateLocation(Config.Movements);
state = FishingState.WaitingMovement;
}
else
@ -148,7 +264,7 @@ namespace MinecraftClient.ChatBots
}
else
{
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.DurabilityCheck;
goto case FishingState.DurabilityCheck;
}
@ -167,7 +283,7 @@ namespace MinecraftClient.ChatBots
case FishingState.DurabilityCheck:
if (DurabilityCheck())
{
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
break;
@ -181,7 +297,7 @@ namespace MinecraftClient.ChatBots
{
if (entity.Type == EntityType.FishingBobber && entity.ObjectData == GetPlayerEntityID())
{
if (Settings.AutoFishing_LogFishingBobber)
if (Config.Log_Fish_Bobber)
LogToConsole(string.Format("FishingBobber spawn at {0}, distance = {1:0.00}", entity.Location, GetCurrentLocation().Distance(entity.Location)));
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.throw"));
@ -202,20 +318,20 @@ namespace MinecraftClient.ChatBots
{
if (entity != null && entity.Type == EntityType.FishingBobber && entity.ID == fishingBobber!.ID)
{
if (Settings.AutoFishing_LogFishingBobber)
if (Config.Log_Fish_Bobber)
LogToConsole(string.Format("FishingBobber despawn at {0}", entity.Location));
if (isFishing)
{
isFishing = false;
if (Settings.AutoFishing_Antidespawn)
if (Config.Antidespawn)
{
LogToConsoleTranslated("bot.autoFish.despawn");
lock (stateLock)
{
counter = (int)(Settings.AutoFishing_CastDelay * 10);
counter = (int)(Config.Cast_Delay * 10);
state = FishingState.WaitingToCast;
}
}
@ -233,12 +349,12 @@ namespace MinecraftClient.ChatBots
double Dz = LastPos.Z - Pos.Z;
LastPos = Pos;
if (Settings.AutoFishing_LogFishingBobber)
if (Config.Log_Fish_Bobber)
LogToConsole(string.Format("FishingBobber {0} Dx={1:0.000000} Dy={2:0.000000} Dz={3:0.000000}", Pos, Dx, Math.Abs(Dy), Dz));
if (Math.Abs(Dx) < Math.Abs(Settings.AutoFishing_StationaryThreshold) &&
Math.Abs(Dz) < Math.Abs(Settings.AutoFishing_StationaryThreshold) &&
Math.Abs(Dy) > Math.Abs(Settings.AutoFishing_HookThreshold))
if (Math.Abs(Dx) < Math.Abs(Config.Stationary_Threshold) &&
Math.Abs(Dz) < Math.Abs(Config.Stationary_Threshold) &&
Math.Abs(Dy) > Math.Abs(Config.Hook_Threshold))
{
// prevent triggering multiple time
if ((DateTime.Now - CaughtTime).TotalSeconds > 1)
@ -283,7 +399,7 @@ namespace MinecraftClient.ChatBots
public void OnCaughtFish()
{
++fishCount;
if (Settings.AutoFishing_Location != null)
if (Config.Movements.Length > 0)
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.caught_at",
fishingBobber!.Location.X, fishingBobber!.Location.Y, fishingBobber!.Location.Z, fishCount));
else
@ -298,47 +414,36 @@ namespace MinecraftClient.ChatBots
}
}
private void UpdateLocation(double[,] locationList)
private void UpdateLocation(LocationConfig[] locationList)
{
if (curLocationIdx >= locationList.GetLength(0))
if (curLocationIdx >= locationList.Length)
{
curLocationIdx = Math.Max(0, locationList.GetLength(0) - 2);
curLocationIdx = Math.Max(0, locationList.Length - 2);
moveDir = -1;
}
else if (curLocationIdx < 0)
{
curLocationIdx = Math.Min(locationList.GetLength(0) - 1, 1);
curLocationIdx = Math.Min(locationList.Length - 1, 1);
moveDir = 1;
}
int locationType = locationList.GetLength(1);
LocationConfig curConfig = locationList[curLocationIdx];
if (locationType == 2)
{
nextYaw = (float)locationList[curLocationIdx, 0];
nextPitch = (float)locationList[curLocationIdx, 1];
}
else if (locationType == 3)
{
nextYaw = GetYaw();
nextPitch = GetPitch();
}
else if (locationType == 5)
{
nextYaw = (float)locationList[curLocationIdx, 3];
nextPitch = (float)locationList[curLocationIdx, 4];
}
if (curConfig.facing != null)
(nextYaw, nextPitch) = (curConfig.facing.Value.yaw, curConfig.facing.Value.pitch);
else
(nextYaw, nextPitch) = (GetYaw(), GetPitch());
if (locationType == 3 || locationType == 5)
if (curConfig.XYZ != null)
{
Location current = GetCurrentLocation();
Location goal = new(locationList[curLocationIdx, 0], locationList[curLocationIdx, 1], locationList[curLocationIdx, 2]);
Location goal = new(curConfig.XYZ.Value.x, curConfig.XYZ.Value.y, curConfig.XYZ.Value.z);
bool isMoveSuccessed;
if (!Movement.CheckChunkLoading(GetWorld(), current, goal))
{
LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z));
isMoveSuccessed = false;
LogToConsole(Translations.Get("cmd.move.chunk_not_loaded", goal.X, goal.Y, goal.Z));
}
else
{
@ -347,8 +452,7 @@ namespace MinecraftClient.ChatBots
if (!isMoveSuccessed)
{
nextYaw = GetYaw();
nextPitch = GetPitch();
(nextYaw, nextPitch) = (GetYaw(), GetPitch());
LogToConsole(Translations.Get("cmd.move.fail", goal));
}
else
@ -365,13 +469,13 @@ namespace MinecraftClient.ChatBots
if (!inventoryEnabled)
return true;
bool useMainHand = Settings.AutoFishing_Mainhand;
bool useMainHand = Config.Mainhand;
Container container = GetPlayerInventory();
int itemSolt = useMainHand ? GetCurrentSlot() + 36 : 45;
if (container.Items.TryGetValue(itemSolt, out Item? handItem) &&
handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Settings.AutoFishing_DurabilityLimit)
handItem.Type == ItemType.FishingRod && (64 - handItem.Damage) >= Config.Durability_Limit)
{
isWaitingRod = false;
return true;
@ -381,11 +485,11 @@ namespace MinecraftClient.ChatBots
if (!isWaitingRod)
LogToConsole(GetTimestamp() + ": " + Translations.Get("bot.autoFish.no_rod"));
if (Settings.AutoFishing_AutoRodSwitch)
if (Config.Auto_Rod_Switch)
{
foreach ((int slot, Item item) in container.Items)
{
if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Settings.AutoFishing_DurabilityLimit)
if (item.Type == ItemType.FishingRod && (64 - item.Damage) >= Config.Durability_Limit)
{
WindowAction(0, slot, WindowActionType.LeftClick);
WindowAction(0, itemSolt, WindowActionType.LeftClick);

View file

@ -1,5 +1,6 @@
using System;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -8,11 +9,59 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class AutoRelog : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoRelog";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.AutoRelog.Delay$")]
public Range Delay = new(10);
[TomlInlineComment("$config.ChatBot.AutoRelog.Retries$")]
public int Retries = 3;
[TomlInlineComment("$config.ChatBot.AutoRelog.Ignore_Kick_Message$")]
public bool Ignore_Kick_Message = false;
[TomlInlineComment("$config.ChatBot.AutoRelog.Kick_Messages_File$")]
public string Kick_Messages_File = @"kickmessages.txt";
public void OnSettingUpdate()
{
if (Delay.min > Delay.max)
(Delay.min, Delay.max) = (Delay.max, Delay.min);
if (Retries == -1)
Retries = int.MaxValue;
}
public struct Range
{
public int min, max;
public Range(int value)
{
value = Math.Max(value, 1);
min = max = value;
}
public Range(int min, int max)
{
min = Math.Max(min, 1);
max = Math.Max(max, 1);
this.min = min;
this.max = max;
}
}
}
private static readonly Random random = new();
private string[] dictionary = Array.Empty<string>();
private readonly int attempts;
private readonly int delayMin;
private readonly int delayMax;
/// <summary>
/// This bot automatically re-join the server if kick message contains predefined string
@ -20,34 +69,25 @@ namespace MinecraftClient.ChatBots
/// <param name="DelayBeforeRelogMin">Minimum delay before re-joining the server (in seconds)</param>
/// <param name="DelayBeforeRelogMax">Maximum delay before re-joining the server (in seconds)</param>
/// <param name="retries">Number of retries if connection fails (-1 = infinite)</param>
public AutoRelog(int DelayBeforeRelogMin, int DelayBeforeRelogMax, int retries)
public AutoRelog()
{
attempts = retries;
if (attempts == -1) { attempts = int.MaxValue; }
McClient.ReconnectionAttemptsLeft = attempts;
delayMin = DelayBeforeRelogMin;
delayMax = DelayBeforeRelogMax;
if (delayMin < 1)
delayMin = 1;
if (delayMax < delayMin)
delayMax = delayMin;
LogDebugToConsoleTranslated("bot.autoRelog.launch", attempts);
LogDebugToConsoleTranslated("bot.autoRelog.launch", Config.Retries);
}
public override void Initialize()
{
McClient.ReconnectionAttemptsLeft = attempts;
if (Settings.AutoRelog_IgnoreKickMessage)
McClient.ReconnectionAttemptsLeft = Config.Retries;
if (Config.Ignore_Kick_Message)
{
LogDebugToConsoleTranslated("bot.autoRelog.no_kick_msg");
}
else
{
if (System.IO.File.Exists(Settings.AutoRelog_KickMessagesFile))
if (System.IO.File.Exists(Config.Kick_Messages_File))
{
LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile));
LogDebugToConsoleTranslated("bot.autoRelog.loading", System.IO.Path.GetFullPath(Config.Kick_Messages_File));
dictionary = System.IO.File.ReadAllLines(Settings.AutoRelog_KickMessagesFile, Encoding.UTF8);
dictionary = System.IO.File.ReadAllLines(Config.Kick_Messages_File, Encoding.UTF8);
for (int i = 0; i < dictionary.Length; i++)
{
@ -57,7 +97,7 @@ namespace MinecraftClient.ChatBots
}
else
{
LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Settings.AutoRelog_KickMessagesFile));
LogToConsoleTranslated("bot.autoRelog.not_found", System.IO.Path.GetFullPath(Config.Kick_Messages_File));
LogDebugToConsoleTranslated("bot.autoRelog.curr_dir", System.IO.Directory.GetCurrentDirectory());
}
@ -77,7 +117,7 @@ namespace MinecraftClient.ChatBots
LogDebugToConsoleTranslated("bot.autoRelog.disconnect_msg", message);
if (Settings.AutoRelog_IgnoreKickMessage)
if (Config.Ignore_Kick_Message)
{
LaunchDelayedReconnection(null);
return true;
@ -100,7 +140,7 @@ namespace MinecraftClient.ChatBots
private void LaunchDelayedReconnection(string? msg)
{
int delay = random.Next(delayMin, delayMax);
int delay = random.Next(Config.Delay.min, Config.Delay.max);
LogDebugToConsoleTranslated(String.IsNullOrEmpty(msg) ? "bot.autoRelog.reconnect_always" : "bot.autoRelog.reconnect", msg);
LogToConsoleTranslated("bot.autoRelog.wait", delay);
System.Threading.Thread.Sleep(delay * 1000);
@ -109,9 +149,9 @@ namespace MinecraftClient.ChatBots
public static bool OnDisconnectStatic(DisconnectReason reason, string message)
{
if (Settings.AutoRelog_Enabled)
if (Config.Enabled)
{
AutoRelog bot = new(Settings.AutoRelog_Delay_Min, Settings.AutoRelog_Delay_Max, Settings.AutoRelog_Retries);
AutoRelog bot = new();
bot.Initialize();
return bot.OnDisconnect(reason, message);
}

View file

@ -1,31 +1,50 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Tomlet.Attributes;
using static MinecraftClient.Settings;
namespace MinecraftClient.ChatBots
{
/// <summary>
/// This bot automatically runs actions when a user sends a message matching a specified rule
/// </summary>
class AutoRespond : ChatBot
public class AutoRespond : ChatBot
{
private readonly string matchesFile;
private readonly bool matchColors;
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "AutoRespond";
public bool Enabled = false;
public string Matches_File = @"matches.ini";
[TomlInlineComment("$config.ChatBot.AutoRespond.Match_Colors$")]
public bool Match_Colors = false;
public void OnSettingUpdate()
{
if (!Enabled) return;
if (!File.Exists(Matches_File))
{
LogToConsole(BotName, Translations.TryGet("bot.autoRespond.file_not_found", Path.GetFullPath(Matches_File)));
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
private List<RespondRule>? respondRules;
private enum MessageType { Public, Private, Other };
/// <summary>
/// Create a new AutoRespond bot
/// </summary>
/// <param name="matchesFile">INI File to load matches from</param>
public AutoRespond(string matchesFile, bool matchColors)
{
this.matchesFile = matchesFile;
this.matchColors = matchColors;
}
/// <summary>
/// Describe a respond rule based on a simple match or a regex
/// </summary>
@ -96,7 +115,7 @@ namespace MinecraftClient.ChatBots
string? toSend = null;
if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Bots_Owners.Contains(username.ToLower())))
if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Config.Main.Advanced.BotOwners.Contains(username.ToLower())))
return null;
switch (msgType)
@ -164,7 +183,7 @@ namespace MinecraftClient.ChatBots
/// </summary>
public override void Initialize()
{
if (File.Exists(matchesFile))
if (File.Exists(Config.Matches_File))
{
Regex? matchRegex = null;
string? matchString = null;
@ -175,9 +194,9 @@ namespace MinecraftClient.ChatBots
TimeSpan cooldown = TimeSpan.Zero;
respondRules = new List<RespondRule>();
LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(matchesFile));
LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(Config.Matches_File));
foreach (string lineRAW in File.ReadAllLines(matchesFile, Encoding.UTF8))
foreach (string lineRAW in File.ReadAllLines(Config.Matches_File, Encoding.UTF8))
{
string line = lineRAW.Split('#')[0].Trim();
if (line.Length > 0)
@ -211,8 +230,8 @@ namespace MinecraftClient.ChatBots
case "action": matchAction = argValue; break;
case "actionprivate": matchActionPrivate = argValue; break;
case "actionother": matchActionOther = argValue; break;
case "ownersonly": ownersOnly = Settings.str2bool(argValue); break;
case "cooldown": cooldown = TimeSpan.FromSeconds(Settings.str2int(argValue)); break;
case "ownersonly": ownersOnly = bool.Parse(argValue); break;
case "cooldown": cooldown = TimeSpan.FromSeconds(int.Parse(argValue, NumberStyles.Any, CultureInfo.CurrentCulture)); break;
}
}
}
@ -222,7 +241,7 @@ namespace MinecraftClient.ChatBots
}
else
{
LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(matchesFile));
LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(Config.Matches_File));
UnloadBot(); //No need to keep the bot active
}
}
@ -264,7 +283,7 @@ namespace MinecraftClient.ChatBots
public override void GetText(string text)
{
//Remove colour codes
if (!matchColors)
if (!Config.Match_Colors)
text = GetVerbatim(text);
//Get Message type
@ -277,7 +296,7 @@ namespace MinecraftClient.ChatBots
else message = text;
//Do not process messages sent by the bot itself
if (msgType == MessageType.Other || sender != Settings.Username)
if (msgType == MessageType.Other || sender != InternalConfig.Username)
{
foreach (RespondRule rule in respondRules!)
{

View file

@ -1,5 +1,7 @@
using System;
using System.IO;
using Tomlet.Attributes;
using static System.Net.WebRequestMethods;
namespace MinecraftClient.ChatBots
{
@ -9,13 +11,42 @@ namespace MinecraftClient.ChatBots
public class ChatLog : ChatBot
{
public enum MessageFilter { AllText, AllMessages, OnlyChat, OnlyWhispers, OnlyInternalCommands };
private readonly bool dateandtime;
private readonly bool saveOther = true;
private readonly bool saveChat = true;
private readonly bool savePrivate = true;
private readonly bool saveInternal = true;
private readonly string logfile;
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "ChatLog";
public bool Enabled = false;
public bool Add_DateTime = true;
public string Log_File = @"chatlog-%username%-%serverip%.txt";
public MessageFilter Filter = MessageFilter.messages;
public void OnSettingUpdate()
{
if (!Enabled) return;
string Log_File_Full = Settings.Config.AppVar.ExpandVars(Log_File);
if (String.IsNullOrEmpty(Log_File_Full) || Log_File_Full.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.chatLog.invalid_file", Log_File_Full));
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
public enum MessageFilter { all, messages, chat, private_chat, internal_msg };
}
private bool saveOther = true;
private bool saveChat = true;
private bool savePrivate = true;
private bool saveInternal = true;
private readonly object logfileLock = new();
/// <summary>
@ -25,59 +56,42 @@ namespace MinecraftClient.ChatBots
/// <param name="filter">The kind of messages to save</param>
/// <param name="AddDateAndTime">Add a date and time before each message</param>
public ChatLog(string file, MessageFilter filter, bool AddDateAndTime)
public ChatLog()
{
UpdateFilter(Config.Filter);
}
public void UpdateFilter(Configs.MessageFilter filter)
{
dateandtime = AddDateAndTime;
logfile = file;
switch (filter)
{
case MessageFilter.AllText:
case Configs.MessageFilter.all:
saveOther = true;
savePrivate = true;
saveChat = true;
break;
case MessageFilter.AllMessages:
case Configs.MessageFilter.messages:
saveOther = false;
savePrivate = true;
saveChat = true;
break;
case MessageFilter.OnlyChat:
case Configs.MessageFilter.chat:
saveOther = false;
savePrivate = false;
saveChat = true;
break;
case MessageFilter.OnlyWhispers:
case Configs.MessageFilter.private_chat:
saveOther = false;
savePrivate = true;
saveChat = false;
break;
case MessageFilter.OnlyInternalCommands:
case Configs.MessageFilter.internal_msg:
saveOther = false;
savePrivate = false;
saveChat = false;
saveInternal = true;
break;
}
if (String.IsNullOrEmpty(file) || file.IndexOfAny(Path.GetInvalidPathChars()) >= 0)
{
LogToConsoleTranslated("bot.chatLog.invalid_file", file);
UnloadBot();
}
}
public static MessageFilter str2filter(string filtername)
{
return Settings.ToLowerIfNeed(filtername) switch
{
#pragma warning disable format // @formatter:off
"all" => MessageFilter.AllText,
"messages" => MessageFilter.AllMessages,
"chat" => MessageFilter.OnlyChat,
"private" => MessageFilter.OnlyWhispers,
"internal" => MessageFilter.OnlyInternalCommands,
_ => MessageFilter.AllText,
#pragma warning restore format // @formatter:on
};
}
public override void GetText(string text)
@ -110,14 +124,15 @@ namespace MinecraftClient.ChatBots
private void Save(string tosave)
{
if (dateandtime)
if (Config.Add_DateTime)
tosave = GetTimestamp() + ' ' + tosave;
string Log_File_Full = Settings.Config.AppVar.ExpandVars(Config.Log_File);
lock (logfileLock)
{
string? directory = Path.GetDirectoryName(logfile);
string? directory = Path.GetDirectoryName(Log_File_Full);
if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory))
Directory.CreateDirectory(directory);
FileStream stream = new(logfile, FileMode.OpenOrCreate);
FileStream stream = new(Log_File_Full, FileMode.OpenOrCreate);
StreamWriter writer = new(stream);
stream.Seek(0, SeekOrigin.End);
writer.WriteLine(tosave);

View file

@ -1,23 +1,42 @@
using System;
using System.Linq;
using MinecraftClient.Mapping;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
public class FollowPlayer : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "FollowPlayer";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.FollowPlayer.Update_Limit$")]
public int Update_Limit = 10;
[TomlInlineComment("$config.ChatBot.FollowPlayer.Stop_At_Distance$")]
public double Stop_At_Distance = 3.0;
public void OnSettingUpdate()
{
if (Update_Limit < 0)
Update_Limit = 0;
if (Stop_At_Distance < 0)
Stop_At_Distance = 0;
}
}
private string? _playerToFollow = null;
private int _updateCounter = 0;
private readonly int _updateLimit;
private readonly int _stopAtDistance;
private bool _unsafeEnabled = false;
public FollowPlayer(int updateLimit = 15, int stopAtDistance = 3)
{
_updateLimit = updateLimit;
_stopAtDistance = stopAtDistance;
}
public override void Initialize()
{
if (!GetEntityHandlingEnabled())
@ -94,7 +113,7 @@ namespace MinecraftClient.ChatBots
public override void OnEntityMove(Entity entity)
{
if (_updateCounter < _updateLimit)
if (_updateCounter < Config.Update_Limit)
return;
_updateCounter = 0;
@ -114,7 +133,7 @@ namespace MinecraftClient.ChatBots
// Stop at specified distance from plater (prevents pushing player around)
double distance = entity.Location.Distance(GetCurrentLocation());
if (distance < _stopAtDistance)
if (distance < Config.Stop_At_Distance)
return;
MoveToLocation(entity.Location, _unsafeEnabled);

View file

@ -1,5 +1,6 @@
using System;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -9,26 +10,33 @@ namespace MinecraftClient.ChatBots
public class HangmanGame : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "HangmanGame";
public bool Enabled = false;
public bool English = true;
public string FileWords_EN = "hangman-en.txt";
public string FileWords_FR = "hangman-fr.txt";
public void OnSettingUpdate() { }
}
private int vie = 0;
private readonly int vie_param = 10;
private int compteur = 0;
private readonly int compteur_param = 3000; //5 minutes
private bool running = false;
private bool[] discovered;
private bool[] discovered = Array.Empty<bool>();
private string word = "";
private string letters = "";
private readonly bool English;
/// <summary>
/// Le jeu du Pendu / Hangman Game
/// </summary>
/// <param name="english">if true, the game will be in english. If false, the game will be in french.</param>
public HangmanGame(bool english)
{
English = english;
discovered = Array.Empty<bool>();
}
public override void Update()
{
@ -40,8 +48,8 @@ namespace MinecraftClient.ChatBots
}
else
{
SendText(English ? "You took too long to try a letter." : "Temps imparti écoulé !");
SendText(English ? "Game canceled." : "Partie annulée.");
SendText(Config.English ? "You took too long to try a letter." : "Temps imparti écoulé !");
SendText(Config.English ? "Game canceled." : "Partie annulée.");
running = false;
}
}
@ -55,7 +63,7 @@ namespace MinecraftClient.ChatBots
if (IsPrivateMessage(text, ref message, ref username))
{
if (Settings.Bots_Owners.Contains(username.ToLower()))
if (Settings.Config.Main.Advanced.BotOwners.Contains(username.ToLower()))
{
switch (message)
{
@ -81,7 +89,7 @@ namespace MinecraftClient.ChatBots
{
if (letters.Contains(letter))
{
SendText(English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé."));
SendText(Config.English ? ("Letter " + letter + " has already been tried.") : ("Le " + letter + " a déjà été proposé."));
}
else
{
@ -91,29 +99,29 @@ namespace MinecraftClient.ChatBots
if (word.Contains(letter))
{
for (int i = 0; i < word.Length; i++) { if (word[i] == letter) { discovered[i] = true; } }
SendText(English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)"));
SendText(Config.English ? ("Yes, the word contains a " + letter + '!') : ("Le " + letter + " figurait bien dans le mot :)"));
}
else
{
vie--;
if (vie == 0)
{
SendText(English ? "Game Over! :]" : "Perdu ! Partie terminée :]");
SendText(English ? ("The word was: " + word) : ("Le mot était : " + word));
SendText(Config.English ? "Game Over! :]" : "Perdu ! Partie terminée :]");
SendText(Config.English ? ("The word was: " + word) : ("Le mot était : " + word));
running = false;
}
else SendText(English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non."));
else SendText(Config.English ? ("The " + letter + "? No.") : ("Le " + letter + " ? Non."));
}
if (running)
{
SendText(English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
SendText(Config.English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
: ("Mot mystère : " + WordCached + " (vie : " + vie + ")"));
}
if (Winner)
{
SendText(English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !"));
SendText(Config.English ? ("Congrats, " + username + '!') : ("Félicitations, " + username + " !"));
running = false;
}
}
@ -132,23 +140,23 @@ namespace MinecraftClient.ChatBots
compteur = compteur_param;
discovered = new bool[word.Length];
SendText(English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio");
SendText(English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
SendText(Config.English ? "Hangman v1.0 - By ORelio" : "Pendu v1.0 - Par ORelio");
SendText(Config.English ? ("Mysterious word: " + WordCached + " (lives : " + vie + ")")
: ("Mot mystère : " + WordCached + " (vie : " + vie + ")"));
SendText(English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)"));
SendText(Config.English ? ("Try some letters ... :)") : ("Proposez une lettre ... :)"));
}
private string Chooseword()
{
if (System.IO.File.Exists(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR))
if (System.IO.File.Exists(Config.English ? Config.FileWords_EN : Config.FileWords_FR))
{
string[] dico = System.IO.File.ReadAllLines(English ? Settings.Hangman_FileWords_EN : Settings.Hangman_FileWords_FR, Encoding.UTF8);
string[] dico = System.IO.File.ReadAllLines(Config.English ? Config.FileWords_EN : Config.FileWords_FR, Encoding.UTF8);
return dico[new Random().Next(dico.Length)];
}
else
{
LogToConsole(English ? "File not found: " + Settings.Hangman_FileWords_EN : "Fichier introuvable : " + Settings.Hangman_FileWords_FR);
return English ? "WORDSAREMISSING" : "DICOMANQUANT";
LogToConsole(Config.English ? "File not found: " + Config.FileWords_EN : "Fichier introuvable : " + Config.FileWords_FR);
return Config.English ? "WORDSAREMISSING" : "DICOMANQUANT";
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -11,6 +12,60 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class Mailer : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Mailer";
public bool Enabled = false;
public string DatabaseFile = "MailerDatabase.ini";
public string IgnoreListFile = "MailerIgnoreList.ini";
public bool PublicInteractions = false;
public int MaxMailsPerPlayer = 10;
public int MaxDatabaseSize = 10000;
public int MailRetentionDays = 30;
public void OnSettingUpdate()
{
if (!Enabled) return;
bool checkSuccessed = true;
if (Config.MaxDatabaseSize <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.db_size"));
checkSuccessed = false;
}
if (Config.MaxMailsPerPlayer <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.max_mails"));
checkSuccessed = false;
}
if (Config.MailRetentionDays <= 0)
{
LogToConsole(BotName, Translations.TryGet("bot.mailer.init_fail.mail_retention"));
checkSuccessed = false;
}
if (!checkSuccessed)
{
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
/// <summary>
/// Holds the list of ignored players
/// </summary>
@ -161,58 +216,37 @@ namespace MinecraftClient.ChatBots
public override void Initialize()
{
LogDebugToConsoleTranslated("bot.mailer.init");
LogDebugToConsoleTranslated("bot.mailer.init.db" + Settings.Mailer_DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.init.ignore" + Settings.Mailer_IgnoreListFile);
LogDebugToConsoleTranslated("bot.mailer.init.public" + Settings.Mailer_PublicInteractions);
LogDebugToConsoleTranslated("bot.mailer.init.max_mails" + Settings.Mailer_MaxMailsPerPlayer);
LogDebugToConsoleTranslated("bot.mailer.init.db_size" + Settings.Mailer_MaxDatabaseSize);
LogDebugToConsoleTranslated("bot.mailer.init.mail_retention" + Settings.Mailer_MailRetentionDays + " days");
LogDebugToConsoleTranslated("bot.mailer.init.db" + Config.DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.init.ignore" + Config.IgnoreListFile);
LogDebugToConsoleTranslated("bot.mailer.init.public" + Config.PublicInteractions);
LogDebugToConsoleTranslated("bot.mailer.init.max_mails" + Config.MaxMailsPerPlayer);
LogDebugToConsoleTranslated("bot.mailer.init.db_size" + Config.MaxDatabaseSize);
LogDebugToConsoleTranslated("bot.mailer.init.mail_retention" + Config.MailRetentionDays + " days");
if (Settings.Mailer_MaxDatabaseSize <= 0)
if (!File.Exists(Config.DatabaseFile))
{
LogToConsoleTranslated("bot.mailer.init_fail.db_size");
UnloadBot();
return;
LogToConsoleTranslated("bot.mailer.create.db", Path.GetFullPath(Config.DatabaseFile));
new MailDatabase().SaveToFile(Config.DatabaseFile);
}
if (Settings.Mailer_MaxMailsPerPlayer <= 0)
if (!File.Exists(Config.IgnoreListFile))
{
LogToConsoleTranslated("bot.mailer.init_fail.max_mails");
UnloadBot();
return;
}
if (Settings.Mailer_MailRetentionDays <= 0)
{
LogToConsoleTranslated("bot.mailer.init_fail.mail_retention");
UnloadBot();
return;
}
if (!File.Exists(Settings.Mailer_DatabaseFile))
{
LogToConsoleTranslated("bot.mailer.create.db", Path.GetFullPath(Settings.Mailer_DatabaseFile));
new MailDatabase().SaveToFile(Settings.Mailer_DatabaseFile);
}
if (!File.Exists(Settings.Mailer_IgnoreListFile))
{
LogToConsoleTranslated("bot.mailer.create.ignore", Path.GetFullPath(Settings.Mailer_IgnoreListFile));
new IgnoreList().SaveToFile(Settings.Mailer_IgnoreListFile);
LogToConsoleTranslated("bot.mailer.create.ignore", Path.GetFullPath(Config.IgnoreListFile));
new IgnoreList().SaveToFile(Config.IgnoreListFile);
}
lock (readWriteLock)
{
LogDebugToConsoleTranslated("bot.mailer.load.db", Path.GetFullPath(Settings.Mailer_DatabaseFile));
mailDatabase = MailDatabase.FromFile(Settings.Mailer_DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.load.db", Path.GetFullPath(Config.DatabaseFile));
mailDatabase = MailDatabase.FromFile(Config.DatabaseFile);
LogDebugToConsoleTranslated("bot.mailer.load.ignore", Path.GetFullPath(Settings.Mailer_IgnoreListFile));
ignoreList = IgnoreList.FromFile(Settings.Mailer_IgnoreListFile);
LogDebugToConsoleTranslated("bot.mailer.load.ignore", Path.GetFullPath(Config.IgnoreListFile));
ignoreList = IgnoreList.FromFile(Config.IgnoreListFile);
}
//Initialize file monitors. In case the bot needs to unload for some reason in the future, do not forget to .Dispose() them
mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Settings.Mailer_DatabaseFile)!, Path.GetFileName(Settings.Mailer_DatabaseFile), FileMonitorCallback);
ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Settings.Mailer_IgnoreListFile)!, Path.GetFileName(Settings.Mailer_IgnoreListFile), FileMonitorCallback);
mailDbFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.DatabaseFile)!, Path.GetFileName(Config.DatabaseFile), FileMonitorCallback);
ignoreListFileMonitor = new FileMonitor(Path.GetDirectoryName(Config.IgnoreListFile)!, Path.GetFileName(Config.IgnoreListFile), FileMonitorCallback);
RegisterChatBotCommand("mailer", Translations.Get("bot.mailer.cmd"), "mailer <getmails|addignored|getignored|removeignored>", ProcessInternalCommand);
}
@ -224,7 +258,7 @@ namespace MinecraftClient.ChatBots
{
maxMessageLength = GetMaxChatMessageLength()
- 44 // Deduct length of "/ 16CharPlayerName 16CharPlayerName mailed: "
- Settings.PrivateMsgsCmdName.Length; // Deduct length of "tell" command
- Settings.Config.Main.Advanced.PrivateMsgsCmdName.Length; // Deduct length of "tell" command
}
/// <summary>
@ -236,7 +270,7 @@ namespace MinecraftClient.ChatBots
string username = "";
text = GetVerbatim(text);
if (IsPrivateMessage(text, ref message, ref username) || (Settings.Mailer_PublicInteractions && IsChatMessage(text, ref message, ref username)))
if (IsPrivateMessage(text, ref message, ref username) || (Config.PublicInteractions && IsChatMessage(text, ref message, ref username)))
{
string usernameLower = username.ToLower();
if (!ignoreList.Contains(usernameLower))
@ -247,8 +281,8 @@ namespace MinecraftClient.ChatBots
case "mail":
case "tellonym":
if (usernameLower != GetUsername().ToLower()
&& mailDatabase.Count < Settings.Mailer_MaxDatabaseSize
&& mailDatabase.Where(mail => mail.SenderLowercase == usernameLower).Count() < Settings.Mailer_MaxMailsPerPlayer)
&& mailDatabase.Count < Config.MaxDatabaseSize
&& mailDatabase.Where(mail => mail.SenderLowercase == usernameLower).Count() < Config.MaxMailsPerPlayer)
{
Queue<string> args = new(Command.GetArgs(message));
if (args.Count >= 2)
@ -266,7 +300,7 @@ namespace MinecraftClient.ChatBots
lock (readWriteLock)
{
mailDatabase.Add(mail);
mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile);
mailDatabase.SaveToFile(Config.DatabaseFile);
}
SendPrivateMessage(username, "Message saved!");
}
@ -307,8 +341,8 @@ namespace MinecraftClient.ChatBots
lock (readWriteLock)
{
mailDatabase.RemoveAll(mail => mail.Delivered);
mailDatabase.RemoveAll(mail => mail.DateSent.AddDays(Settings.Mailer_MailRetentionDays) < DateTime.Now);
mailDatabase.SaveToFile(Settings.Mailer_DatabaseFile);
mailDatabase.RemoveAll(mail => mail.DateSent.AddDays(Config.MailRetentionDays) < DateTime.Now);
mailDatabase.SaveToFile(Config.DatabaseFile);
}
nextMailSend = dateNow.AddSeconds(10);
@ -324,8 +358,8 @@ namespace MinecraftClient.ChatBots
{
lock (readWriteLock)
{
mailDatabase = MailDatabase.FromFile(Settings.Mailer_DatabaseFile);
ignoreList = IgnoreList.FromFile(Settings.Mailer_IgnoreListFile);
mailDatabase = MailDatabase.FromFile(Config.DatabaseFile);
ignoreList = IgnoreList.FromFile(Config.IgnoreListFile);
}
}
@ -357,7 +391,7 @@ namespace MinecraftClient.ChatBots
if (!ignoreList.Contains(username))
{
ignoreList.Add(username);
ignoreList.SaveToFile(Settings.Mailer_IgnoreListFile);
ignoreList.SaveToFile(Config.IgnoreListFile);
}
}
return Translations.Get("bot.mailer.cmd.ignore.added", args[1]);
@ -369,7 +403,7 @@ namespace MinecraftClient.ChatBots
if (ignoreList.Contains(username))
{
ignoreList.Remove(username);
ignoreList.SaveToFile(Settings.Mailer_IgnoreListFile);
ignoreList.SaveToFile(Config.IgnoreListFile);
}
}
return Translations.Get("bot.mailer.cmd.ignore.removed", args[1]);

View file

@ -5,41 +5,59 @@ using System.Globalization;
using System.IO;
using MinecraftClient.Mapping;
using MinecraftClient.Protocol.Handlers;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
class Map : ChatBot
public class Map : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "Map";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.Map.Should_Resize$")]
public bool Should_Resize = false;
[TomlInlineComment("$config.ChatBot.Map.Resize_To$")]
public int Resize_To = 256;
[TomlInlineComment("$config.ChatBot.Map.Auto_Render_On_Update$")]
public bool Auto_Render_On_Update = false;
[TomlInlineComment("$config.ChatBot.Map.Delete_All_On_Unload$")]
public bool Delete_All_On_Unload = true;
[TomlInlineComment("$config.ChatBot.Map.Notify_On_First_Update$")]
public bool Notify_On_First_Update = true;
public void OnSettingUpdate()
{
if (Resize_To < 128)
Resize_To = 128;
}
}
private readonly string baseDirectory = @"Rendered_Maps";
private readonly Dictionary<int, McMap> cachedMaps = new();
private bool shouldResize = true;
private int resizeTo = 256;
private bool autoRenderOnUpdate = true;
private bool deleteAllOnExit = true;
private bool notifyOnFirstUpdate = true;
public override void Initialize()
{
if (!Directory.Exists(baseDirectory))
Directory.CreateDirectory(baseDirectory);
shouldResize = Settings.Map_Should_Resize;
resizeTo = Settings.Map_Resize_To;
if (resizeTo < 128)
resizeTo = 128;
autoRenderOnUpdate = Settings.Map_Auto_Render_On_Update;
deleteAllOnExit = Settings.Map_Delete_All_On_Unload;
notifyOnFirstUpdate = Settings.Map_Notify_On_First_Update;
RegisterChatBotCommand("maps", "bot.map.cmd.desc", "maps list|render <id> or maps l|r <id>", OnMapCommand);
}
public override void OnUnload()
{
if (deleteAllOnExit)
if (Config.Delete_All_On_Unload)
{
DirectoryInfo di = new(baseDirectory);
FileInfo[] files = di.GetFiles();
@ -124,7 +142,7 @@ namespace MinecraftClient.ChatBots
{
cachedMaps.Add(mapid, map);
if (notifyOnFirstUpdate)
if (Config.Notify_On_First_Update)
LogToConsoleTranslated("bot.map.received_map", map.MapId);
}
else
@ -133,7 +151,7 @@ namespace MinecraftClient.ChatBots
cachedMaps.Add(mapid, map);
}
if (autoRenderOnUpdate)
if (Config.Auto_Render_On_Update)
GenerateMapImage(map);
}
@ -165,8 +183,8 @@ namespace MinecraftClient.ChatBots
// Resize, double the image
if (shouldResize)
image = ResizeBitmap(image, resizeTo, resizeTo);
if (Config.Should_Resize)
image = ResizeBitmap(image, Config.Resize_To, Config.Resize_To);
image.Save(fileName);
LogToConsole(Translations.TryGet("bot.map.rendered", map.MapId, fileName));

View file

@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -10,28 +11,34 @@ namespace MinecraftClient.ChatBots
public class PlayerListLogger : ChatBot
{
private int count;
private readonly int timeping;
private readonly string file;
public static Configs Config = new();
/// <summary>
/// This bot sends a /list command every X seconds and save the result.
/// </summary>
/// <param name="pingparam">Time amount between each list ping (10 = 1s, 600 = 1 minute, etc.)</param>
public PlayerListLogger(int pingparam, string filetosavein)
[TomlDoNotInlineObject]
public class Configs
{
count = 0;
file = filetosavein;
timeping = pingparam;
if (timeping < 10) { timeping = 10; } //To avoid flooding
[NonSerialized]
private const string BotName = "PlayerListLogger";
public bool Enabled = false;
public string File = "playerlog.txt";
[TomlInlineComment("$config.ChatBot.PlayerListLogger.Delay$")]
public int Delay = 600;
public void OnSettingUpdate()
{
if (Delay < 10)
Delay = 10;
}
}
private int count = 0;
public override void Update()
{
count++;
if (count == timeping)
if (count == Config.Delay)
{
DateTime now = DateTime.Now;
@ -40,7 +47,7 @@ namespace MinecraftClient.ChatBots
StringBuilder sb = new();
sb.AppendLine(string.Format("[{0}/{1}/{2} {3}:{4}]", now.Year, now.Month, now.Day, now.Hour, now.Minute));
sb.AppendLine(string.Join(", ", GetOnlinePlayers())).AppendLine();
System.IO.File.AppendAllText(file, sb.ToString());
System.IO.File.AppendAllText(Settings.Config.AppVar.ExpandVars(Config.File), sb.ToString());
count = 0;
}

View file

@ -1,4 +1,5 @@
using System;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -8,11 +9,28 @@ namespace MinecraftClient.ChatBots
public class RemoteControl : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "RemoteControl";
public bool Enabled = false;
public bool AutoTpaccept = true;
public bool AutoTpaccept_Everyone = false;
public void OnSettingUpdate() { }
}
public override void GetText(string text)
{
text = GetVerbatim(text).Trim();
string command = "", sender = "";
if (IsPrivateMessage(text, ref command, ref sender) && Settings.Bots_Owners.Contains(sender.ToLower().Trim()))
if (IsPrivateMessage(text, ref command, ref sender) && Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim()))
{
string? response = "";
PerformInternalCommand(command, ref response);
@ -26,9 +44,9 @@ namespace MinecraftClient.ChatBots
SendPrivateMessage(sender, response);
}
}
else if (Settings.RemoteCtrl_AutoTpaccept
else if (Config.AutoTpaccept
&& IsTeleportRequest(text, ref sender)
&& (Settings.RemoteCtrl_AutoTpaccept_Everyone || Settings.Bots_Owners.Contains(sender.ToLower().Trim())))
&& (Config.AutoTpaccept_Everyone || Settings.Config.Main.Advanced.BotOwners.Contains(sender.ToLower().Trim())))
{
SendText("/tpaccept");
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using MinecraftClient.Protocol;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -9,24 +10,35 @@ namespace MinecraftClient.ChatBots
/// </summary>
public class ReplayCapture : ChatBot
{
private ReplayHandler? replay;
private readonly int backupInterval = 3000; // Unit: second * 10
private int backupCounter = -1;
public static Configs Config = new();
public ReplayCapture(int backupInterval)
[TomlDoNotInlineObject]
public class Configs
{
if (backupInterval != -1)
this.backupInterval = backupInterval * 10;
else
this.backupInterval = -1;
[NonSerialized]
private const string BotName = "ReplayCapture";
public bool Enabled = false;
[TomlInlineComment("$config.ChatBot.ReplayCapture.Backup_Interval$")]
public int Backup_Interval = 3000;
public void OnSettingUpdate()
{
if (Backup_Interval < -1)
Backup_Interval = -1;
}
}
private ReplayHandler? replay;
private int backupCounter = -1;
public override void Initialize()
{
SetNetworkPacketEventEnabled(true);
replay = new ReplayHandler(GetProtocolVersion());
replay.MetaData.serverName = GetServerHost() + GetServerPort();
backupCounter = backupInterval;
backupCounter = Config.Backup_Interval * 10;
RegisterChatBotCommand("replay", Translations.Get("bot.replayCapture.cmd"), "replay <save|stop>", Command);
}
@ -38,12 +50,12 @@ namespace MinecraftClient.ChatBots
public override void Update()
{
if (backupInterval > 0 && replay!.RecordRunning)
if (Config.Backup_Interval > 0 && replay!.RecordRunning)
{
if (backupCounter <= 0)
{
replay.CreateBackupReplay(@"recording_cache\REPLAY_BACKUP.mcpr");
backupCounter = backupInterval;
backupCounter = Config.Backup_Interval * 10;
}
else backupCounter--;
}

View file

@ -107,7 +107,7 @@ namespace MinecraftClient.ChatBots
}
}
if (Settings.DebugMessages)
if (Settings.Config.Logging.DebugMessages)
{
string caller = "Script";
try
@ -195,7 +195,7 @@ namespace MinecraftClient.ChatBots
{
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
{
instruction_line = Settings.ExpandVars(instruction_line, localVars);
instruction_line = Settings.Config.AppVar.ExpandVars(instruction_line, localVars);
string instruction_name = instruction_line.Split(' ')[0];
switch (instruction_name.ToLower())
{

View file

@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using Tomlet.Attributes;
namespace MinecraftClient.ChatBots
{
@ -11,6 +13,32 @@ namespace MinecraftClient.ChatBots
public class ScriptScheduler : ChatBot
{
public static Configs Config = new();
[TomlDoNotInlineObject]
public class Configs
{
[NonSerialized]
private const string BotName = "ScriptScheduler";
public bool Enabled = false;
public string Tasks_File = @"tasks.ini";
public void OnSettingUpdate()
{
if (!Enabled) return;
string Tasks_File_Full = Settings.Config.AppVar.ExpandVars(Tasks_File);
if (!File.Exists(Tasks_File_Full))
{
LogToConsole(BotName, Translations.TryGet("bot.scriptScheduler.not_found", Path.GetFullPath(Tasks_File_Full)));
LogToConsole(BotName, Translations.TryGet("general.bot_unload"));
Enabled = false;
}
}
}
private class TaskDesc
{
public string? action = null;
@ -27,26 +55,20 @@ namespace MinecraftClient.ChatBots
private static bool firstlogin_done = false;
private readonly string tasksfile;
private bool serverlogin_done;
private bool serverlogin_done = false;
private readonly List<TaskDesc> tasks = new();
private int verifytasks_timeleft = 10;
private readonly int verifytasks_delay = 10;
public ScriptScheduler(string tasksfile)
{
this.tasksfile = tasksfile;
serverlogin_done = false;
}
public override void Initialize()
{
//Load the given file from the startup parameters
if (System.IO.File.Exists(tasksfile))
string Tasks_File_Full = Settings.Config.AppVar.ExpandVars(Config.Tasks_File);
if (File.Exists(Tasks_File_Full))
{
LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(tasksfile));
LogDebugToConsoleTranslated("bot.scriptScheduler.loading", System.IO.Path.GetFullPath(Tasks_File_Full));
TaskDesc? current_task = null;
string[] lines = System.IO.File.ReadAllLines(tasksfile, Encoding.UTF8);
string[] lines = System.IO.File.ReadAllLines(Tasks_File_Full, Encoding.UTF8);
foreach (string lineRAW in lines)
{
string line = lineRAW.Split('#')[0].Trim();
@ -70,10 +92,10 @@ namespace MinecraftClient.ChatBots
string argValue = line[(argName.Length + 1)..];
switch (argName.ToLower())
{
case "triggeronfirstlogin": current_task.triggerOnFirstLogin = Settings.str2bool(argValue); break;
case "triggeronlogin": current_task.triggerOnLogin = Settings.str2bool(argValue); break;
case "triggerontime": current_task.triggerOnTime = Settings.str2bool(argValue); break;
case "triggeroninterval": current_task.triggerOnInterval = Settings.str2bool(argValue); break;
case "triggeronfirstlogin": current_task.triggerOnFirstLogin = bool.Parse(argValue); break;
case "triggeronlogin": current_task.triggerOnLogin = bool.Parse(argValue); break;
case "triggerontime": current_task.triggerOnTime = bool.Parse(argValue); break;
case "triggeroninterval": current_task.triggerOnInterval = bool.Parse(argValue); break;
case "timevalue": try { current_task.triggerOnTime_Times.Add(DateTime.ParseExact(argValue, "HH:mm", CultureInfo.InvariantCulture)); } catch { } break;
case "timeinterval":
int interval;
@ -112,7 +134,7 @@ namespace MinecraftClient.ChatBots
}
else
{
LogToConsoleTranslated("bot.scriptScheduler.not_found", System.IO.Path.GetFullPath(tasksfile));
LogToConsoleTranslated("bot.scriptScheduler.not_found", Path.GetFullPath(Tasks_File_Full));
UnloadBot(); //No need to keep the bot active
}
}