diff --git a/MinecraftClient/ChatBots/AutoRespond.cs b/MinecraftClient/ChatBots/AutoRespond.cs
index 3a0d6575..52a14944 100644
--- a/MinecraftClient/ChatBots/AutoRespond.cs
+++ b/MinecraftClient/ChatBots/AutoRespond.cs
@@ -1,38 +1,209 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using System.IO;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
namespace MinecraftClient.ChatBots
{
+ ///
+ /// This bot automatically runs actions when a user sends a message matching a specified rule
+ ///
class AutoRespond : ChatBot
{
- private string[] respondon = new string[0];
- private string[] torespond = new string[0];
+ private string matchesFile;
+ private List respondRules;
+ private static string header = "[AutoRespond] ";
- //Initalize the bot
+ ///
+ /// Create a new AutoRespond bot
+ ///
+ /// INI File to load matches from
+ public AutoRespond(string matchesFile)
+ {
+ this.matchesFile = matchesFile;
+ }
+
+ ///
+ /// Describe a respond rule based on a simple match or a regex
+ ///
+ private class RespondRule
+ {
+ private Regex regex;
+ private string match;
+ private string actionPublic;
+ private string actionPrivate;
+
+ ///
+ /// Create a respond rule from a regex and a reponse message or command
+ ///
+ /// Regex
+ /// Internal command to run for public messages
+ /// Internal command to run for private messages
+ public RespondRule(Regex regex, string actionPublic, string actionPrivate)
+ {
+ this.regex = regex;
+ this.match = null;
+ this.actionPublic = actionPublic;
+ this.actionPrivate = actionPrivate;
+ }
+
+ ///
+ /// Create a respond rule from a match string and a reponse message or command
+ ///
+ /// Match string
+ /// Internal command to run for public messages
+ /// Internal command to run for private messages
+ public RespondRule(string match, string actionPublic, string actionPrivate)
+ {
+ this.regex = null;
+ this.match = match;
+ this.actionPublic = actionPublic;
+ this.actionPrivate = actionPrivate;
+ }
+
+ ///
+ /// Match the respond rule to the specified string and return a message or command to send if a match is detected
+ ///
+ /// Player who have sent the message
+ /// Message to match against the regex or match string
+ /// True if the provided message was sent privately eg with /tell
+ /// Internal command to run as a response to this user, or null if no match has been detected
+ public string Match(string username, string message, bool privateMsg)
+ {
+ if (regex != null)
+ {
+ if (regex.IsMatch(message))
+ {
+ Match regexMatch = regex.Match(message);
+ string toSend = privateMsg ? actionPrivate : actionPublic;
+ for (int i = regexMatch.Groups.Count - 1; i >= 1; i--)
+ toSend = toSend.Replace("$" + i, regexMatch.Groups[i].Value);
+ toSend = toSend.Replace("$u", username);
+ return toSend;
+ }
+ }
+ else if (!String.IsNullOrEmpty(match))
+ {
+ if (message.Contains(match))
+ {
+ return (privateMsg
+ ? actionPrivate
+ : actionPublic).Replace("$u", username);
+ }
+ }
+ return null;
+ }
+ }
+
+ ///
+ /// Initialize the AutoRespond bot from the matches file
+ ///
public override void Initialize()
{
- respondon = LoadDistinctEntriesFromFile(Settings.Respond_MatchesFile);
- torespond = LoadDistinctEntriesFromFile(Settings.Respond_RespondFile);
- ConsoleIO.WriteLine("Auto Respond Bot Sucessfully loaded!");
+ if (File.Exists(matchesFile))
+ {
+ Regex matchRegex = null;
+ string matchString = null;
+ string matchAction = null;
+ string matchActionPrivate = null;
+ respondRules = new List();
+
+ foreach (string lineRAW in File.ReadAllLines(matchesFile))
+ {
+ string line = lineRAW.Split('#')[0].Trim();
+ if (line.Length > 0)
+ {
+ if (line[0] == '[' && line[line.Length - 1] == ']')
+ {
+ switch (line.Substring(1, line.Length - 2).ToLower())
+ {
+ case "match":
+ CheckAddMatch(matchRegex, matchString, matchAction, matchActionPrivate);
+ matchRegex = null;
+ matchString = null;
+ matchAction = null;
+ matchActionPrivate = null;
+ break;
+ }
+ }
+ else
+ {
+ string argName = line.Split('=')[0];
+ if (line.Length > (argName.Length + 1))
+ {
+ string argValue = line.Substring(argName.Length + 1);
+ switch (argName.ToLower())
+ {
+ case "regex": matchRegex = new Regex(argValue); break;
+ case "match": matchString = argValue; break;
+ case "action": matchAction = argValue; break;
+ case "actionprivate": matchAction = argValue; break;
+ }
+ }
+ }
+ }
+ }
+ CheckAddMatch(matchRegex, matchString, matchAction, matchActionPrivate);
+ }
+ else
+ {
+ LogToConsole("File not found: '" + matchesFile + "'");
+ UnloadBot(); //No need to keep the bot active
+ }
+ }
+
+ ///
+ /// Create a new respond rule from the provided arguments, only if they are valid: at least one match and one action
+ ///
+ /// Matching regex
+ /// Matching string
+ /// Action if the matching message is public
+ /// Action if the matching message is private
+ private void CheckAddMatch(Regex matchRegex, string matchString, string matchAction, string matchActionPrivate)
+ {
+ if (matchAction != null || matchActionPrivate != null)
+ {
+ if (matchActionPrivate == null)
+ {
+ matchActionPrivate = matchAction;
+ }
+
+ if (matchRegex != null)
+ {
+ respondRules.Add(new RespondRule(matchRegex, matchAction, matchActionPrivate));
+ }
+ else if (matchString != null)
+ {
+ respondRules.Add(new RespondRule(matchString, matchAction, matchActionPrivate));
+ }
+ }
}
public override void GetText(string text)
{
//Remove colour codes
text = getVerbatim(text).ToLower();
- //Check text to see if bot should respond
- foreach (string alert in respondon.Where(alert => text.Contains(alert)))
+
+ //Check if this is a valid message
+ string sender = "", message = "";
+ bool chatMessage = isChatMessage(text, ref message, ref sender);
+ bool privateMessage = false;
+ if (!chatMessage)
+ privateMessage = isPrivateMessage(text, ref message, ref sender);
+
+ //Process only chat messages sent by another user
+ if ((chatMessage || privateMessage) && sender != Settings.Username)
{
- //Find what to respond with
- for (int x = 0; x < respondon.Length; x++)
+ foreach (RespondRule rule in respondRules)
{
- if (respondon[x].ToString().Contains(alert))
+ string toPerform = rule.Match(sender, message, privateMessage);
+ if (toPerform != null)
{
- //Respond
- SendText(torespond[x].ToString());
+ string response = null;
+ LogToConsole(header + toPerform);
+ performInternalCommand(toPerform, ref response);
+ if (!String.IsNullOrEmpty(response))
+ LogToConsole(header + response);
}
}
}
diff --git a/MinecraftClient/McTcpClient.cs b/MinecraftClient/McTcpClient.cs
index 3f152002..46b40c59 100644
--- a/MinecraftClient/McTcpClient.cs
+++ b/MinecraftClient/McTcpClient.cs
@@ -106,7 +106,7 @@ namespace MinecraftClient
if (Settings.AutoRelog_Enabled) { BotLoad(new ChatBots.AutoRelog(Settings.AutoRelog_Delay, Settings.AutoRelog_Retries)); }
if (Settings.ScriptScheduler_Enabled) { BotLoad(new ChatBots.ScriptScheduler(Settings.expandVars(Settings.ScriptScheduler_TasksFile))); }
if (Settings.RemoteCtrl_Enabled) { BotLoad(new ChatBots.RemoteControl()); }
- if (Settings.Respond_Enabled) { BotLoad(new ChatBots.AutoRespond()); }
+ if (Settings.AutoRespond_Enabled) { BotLoad(new ChatBots.AutoRespond(Settings.AutoRespond_Matches)); }
}
try
diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs
index 4c4779a7..b7daa524 100644
--- a/MinecraftClient/Settings.cs
+++ b/MinecraftClient/Settings.cs
@@ -90,16 +90,15 @@ namespace MinecraftClient
public static bool RemoteCtrl_AutoTpaccept_Everyone = false;
//Auto Respond
- public static bool Respond_Enabled = false;
- public static string Respond_MatchesFile = "detect.txt";
- public static string Respond_RespondFile = "respond.txt";
+ public static bool AutoRespond_Enabled = false;
+ public static string AutoRespond_Matches = "matches.ini";
//Custom app variables and Minecraft accounts
private static Dictionary AppVars = new Dictionary();
private static Dictionary> Accounts = new Dictionary>();
private static Dictionary> Servers = new Dictionary>();
- private enum ParseMode { Default, Main, AppVars, Proxy, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, Auto_Respond };
+ private enum ParseMode { Default, Main, AppVars, Proxy, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, AutoRespond };
///
/// Load settings from the give INI file
@@ -133,7 +132,7 @@ namespace MinecraftClient
case "remotecontrol": pMode = ParseMode.RemoteControl; break;
case "proxy": pMode = ParseMode.Proxy; break;
case "appvars": pMode = ParseMode.AppVars; break;
- case "auto respond": pMode = ParseMode.Auto_Respond; break;
+ case "autorespond": pMode = ParseMode.AutoRespond; break;
default: pMode = ParseMode.Default; break;
}
}
@@ -317,12 +316,11 @@ namespace MinecraftClient
setVar(argName, argValue);
break;
- case ParseMode.Auto_Respond:
+ case ParseMode.AutoRespond:
switch (argName.ToLower())
{
- case "enabled": Respond_Enabled = str2bool(argValue); break;
- case "matchfile": Respond_MatchesFile = argValue; break;
- case "respondfile": Respond_RespondFile = argValue; break;
+ case "enabled": AutoRespond_Enabled = str2bool(argValue); break;
+ case "matchesfile": AutoRespond_Matches = argValue; break;
}
break;
}
@@ -422,12 +420,9 @@ namespace MinecraftClient
+ "autotpaccept=true\r\n"
+ "tpaccepteveryone=false\r\n"
+ "\r\n"
- + "[Auto Respond]\r\n"
+ + "[AutoRespond]\r\n"
+ "enabled=false\r\n"
- + "matchfile=detect.txt\r\n"
- + "respondfile=respond.txt\r\n"
- + "#To use the bot, place the text to detect in the matchfile file and the text to respond with in the respondfile\r\n"
- + "#Each line in each file is relevant to the same line in the other document, for example if the bot detects the text in line 1 of the first file, it will respond with line 1 of the second file.\r\n", Encoding.UTF8);
+ + "matchesfile=matches.ini\r\n", Encoding.UTF8);
}
public static int str2int(string str) { try { return Convert.ToInt32(str); } catch { return 0; } }