using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
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 matchesFile;
private bool matchColors;
private List respondRules;
private enum MessageType { Public, Private, Other };
///
/// Create a new AutoRespond bot
///
/// INI File to load matches from
public AutoRespond(string matchesFile, bool matchColors)
{
this.matchesFile = matchesFile;
this.matchColors = matchColors;
}
///
/// 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;
private string actionOther;
private bool ownersOnly;
private TimeSpan cooldown;
private DateTime cooldownExpiration;
///
/// 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
/// Internal command to run for any other messages
/// Only match messages from bot owners
/// Minimal cooldown between two matches
public RespondRule(Regex regex, string actionPublic, string actionPrivate, string actionOther, bool ownersOnly, TimeSpan cooldown)
{
this.regex = regex;
this.match = null;
this.actionPublic = actionPublic;
this.actionPrivate = actionPrivate;
this.actionOther = actionOther;
this.ownersOnly = ownersOnly;
this.cooldown = cooldown;
this.cooldownExpiration = DateTime.MinValue;
}
///
/// 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
/// Only match messages from bot owners
/// Minimal cooldown between two matches
public RespondRule(string match, string actionPublic, string actionPrivate, string actionOther, bool ownersOnly, TimeSpan cooldown)
{
this.regex = null;
this.match = match;
this.actionPublic = actionPublic;
this.actionPrivate = actionPrivate;
this.actionOther = actionOther;
this.ownersOnly = ownersOnly;
this.cooldown = cooldown;
this.cooldownExpiration = DateTime.MinValue;
}
///
/// 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
/// Type of the message public/private message, or other message
/// Dictionary to populate with match variables in case of Regex match
/// 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, MessageType msgType, Dictionary localVars)
{
if (DateTime.Now < cooldownExpiration)
return null;
string toSend = null;
if (ownersOnly && (String.IsNullOrEmpty(username) || !Settings.Bots_Owners.Contains(username.ToLower())))
return null;
switch (msgType)
{
case MessageType.Public: toSend = actionPublic; break;
case MessageType.Private: toSend = actionPrivate; break;
case MessageType.Other: toSend = actionOther; break;
}
if (String.IsNullOrEmpty(toSend))
return null;
if (regex != null)
{
if (regex.IsMatch(message))
{
cooldownExpiration = DateTime.Now + cooldown;
Match regexMatch = regex.Match(message);
localVars["match_0"] = regexMatch.Groups[0].Value;
for (int i = regexMatch.Groups.Count - 1; i >= 1; i--)
{
toSend = toSend.Replace("$" + i, regexMatch.Groups[i].Value);
localVars["match_" + i] = regexMatch.Groups[i].Value;
}
toSend = toSend.Replace("$u", username);
localVars["match_u"] = username;
return toSend;
}
}
else if (!String.IsNullOrEmpty(match))
{
if (message.ToLower().Contains(match.ToLower()))
{
cooldownExpiration = DateTime.Now + cooldown;
localVars["match_0"] = message;
localVars["match_u"] = username;
return toSend.Replace("$u", username);
}
}
return null;
}
///
/// Get a string representation of the RespondRule
///
///
public override string ToString()
{
return Translations.Get(
"bot.autoRespond.match",
match,
regex,
actionPublic,
actionPrivate,
actionOther,
ownersOnly,
(int)cooldown.TotalSeconds
);
}
}
///
/// Initialize the AutoRespond bot from the matches file
///
public override void Initialize()
{
if (File.Exists(matchesFile))
{
Regex matchRegex = null;
string matchString = null;
string matchAction = null;
string matchActionPrivate = null;
string matchActionOther = null;
bool ownersOnly = false;
TimeSpan cooldown = TimeSpan.Zero;
respondRules = new List();
LogDebugToConsoleTranslated("bot.autoRespond.loading", System.IO.Path.GetFullPath(matchesFile));
foreach (string lineRAW in File.ReadAllLines(matchesFile, Encoding.UTF8))
{
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, matchActionOther, ownersOnly, cooldown);
matchRegex = null;
matchString = null;
matchAction = null;
matchActionPrivate = null;
matchActionOther = null;
ownersOnly = false;
cooldown = TimeSpan.Zero;
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": matchActionPrivate = argValue; break;
case "actionother": matchActionOther = argValue; break;
case "ownersonly": ownersOnly = Settings.str2bool(argValue); break;
case "cooldown": cooldown = TimeSpan.FromSeconds(Settings.str2int(argValue)); break;
}
}
}
}
}
CheckAddMatch(matchRegex, matchString, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown);
}
else
{
LogToConsoleTranslated("bot.autoRespond.file_not_found", System.IO.Path.GetFullPath(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
/// Only match messages from bot owners
/// Minimal cooldown between two matches
private void CheckAddMatch(Regex matchRegex, string matchString, string matchAction, string matchActionPrivate, string matchActionOther, bool ownersOnly, TimeSpan cooldown)
{
if (matchRegex != null || matchString != null || matchAction != null || matchActionPrivate != null || matchActionOther != null || ownersOnly || cooldown != TimeSpan.Zero)
{
RespondRule rule = matchRegex != null
? new RespondRule(matchRegex, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown)
: new RespondRule(matchString, matchAction, matchActionPrivate, matchActionOther, ownersOnly, cooldown);
if (matchAction != null || matchActionPrivate != null || matchActionOther != null)
{
if (matchRegex != null || matchString != null)
{
respondRules.Add(rule);
LogDebugToConsoleTranslated("bot.autoRespond.loaded_match", rule);
}
else LogDebugToConsoleTranslated("bot.autoRespond.no_trigger", rule);
}
else LogDebugToConsoleTranslated("bot.autoRespond.no_action", rule);
}
}
///
/// Process messages from the server and test them against all matches
///
/// Text from the server
public override void GetText(string text)
{
//Remove colour codes
if (!this.matchColors)
text = GetVerbatim(text);
//Get Message type
string sender = "", message = "";
MessageType msgType = MessageType.Other;
if (IsChatMessage(text, ref message, ref sender))
msgType = MessageType.Public;
else if (IsPrivateMessage(text, ref message, ref sender))
msgType = MessageType.Private;
else message = text;
//Do not process messages sent by the bot itself
if (msgType == MessageType.Other || sender != Settings.Username)
{
foreach (RespondRule rule in respondRules)
{
Dictionary localVars = new Dictionary();
string toPerform = rule.Match(sender, message, msgType, localVars);
if (!String.IsNullOrEmpty(toPerform))
{
string response = null;
LogToConsoleTranslated("bot.autoRespond.match_run", toPerform);
PerformInternalCommand(toPerform, ref response, localVars);
if (!String.IsNullOrEmpty(response))
LogToConsole(response);
}
}
}
}
}
}