AutoRespond improvements

- Add improvements from pull request #76
- Add support for regexes instead of simple matches
- Add support for internal MCC commands eg script
- Add support for flexible INI file containing matches

TODO: Testing, sample INI file, proper documentation
This commit is contained in:
ORelio 2015-06-11 23:36:35 +02:00
parent 840ac01dc5
commit 0029561135
3 changed files with 197 additions and 31 deletions

View file

@ -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
{
/// <summary>
/// This bot automatically runs actions when a user sends a message matching a specified rule
/// </summary>
class AutoRespond : ChatBot
{
private string[] respondon = new string[0];
private string[] torespond = new string[0];
private string matchesFile;
private List<RespondRule> respondRules;
private static string header = "[AutoRespond] ";
//Initalize the bot
/// <summary>
/// Create a new AutoRespond bot
/// </summary>
/// <param name="matchesFile">INI File to load matches from</param>
public AutoRespond(string matchesFile)
{
this.matchesFile = matchesFile;
}
/// <summary>
/// Describe a respond rule based on a simple match or a regex
/// </summary>
private class RespondRule
{
private Regex regex;
private string match;
private string actionPublic;
private string actionPrivate;
/// <summary>
/// Create a respond rule from a regex and a reponse message or command
/// </summary>
/// <param name="regex">Regex</param>
/// <param name="actionPublic">Internal command to run for public messages</param>
/// <param name="actionPrivate">Internal command to run for private messages</param>
public RespondRule(Regex regex, string actionPublic, string actionPrivate)
{
this.regex = regex;
this.match = null;
this.actionPublic = actionPublic;
this.actionPrivate = actionPrivate;
}
/// <summary>
/// Create a respond rule from a match string and a reponse message or command
/// </summary>
/// <param name="match">Match string</param>
/// <param name="actionPublic">Internal command to run for public messages</param>
/// <param name="actionPrivate">Internal command to run for private messages</param>
public RespondRule(string match, string actionPublic, string actionPrivate)
{
this.regex = null;
this.match = match;
this.actionPublic = actionPublic;
this.actionPrivate = actionPrivate;
}
/// <summary>
/// Match the respond rule to the specified string and return a message or command to send if a match is detected
/// </summary>
/// <param name="username">Player who have sent the message</param>
/// <param name="message">Message to match against the regex or match string</param>
/// <param name="privateMsg">True if the provided message was sent privately eg with /tell</param>
/// <returns>Internal command to run as a response to this user, or null if no match has been detected</returns>
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;
}
}
/// <summary>
/// Initialize the AutoRespond bot from the matches file
/// </summary>
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<RespondRule>();
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
}
}
/// <summary>
/// Create a new respond rule from the provided arguments, only if they are valid: at least one match and one action
/// </summary>
/// <param name="matchRegex">Matching regex</param>
/// <param name="matchString">Matching string</param>
/// <param name="matchAction">Action if the matching message is public</param>
/// <param name="matchActionPrivate">Action if the matching message is private</param>
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);
}
}
}