Give access to AutoRespond matches inside scripts

Available as %match_u%, %match_1% and so on
See #770 for initial suggestion
See #772 for in-progress implementation
This commit is contained in:
ORelio 2020-03-27 21:14:05 +01:00
parent e4e1f0b9fa
commit 00112e4c6a
25 changed files with 129 additions and 69 deletions

View file

@ -25,10 +25,11 @@ namespace MinecraftClient
/// <param name="tickHandler">Tick handler for waiting after some API calls</param>
/// <param name="lines">Lines of the script file to run</param>
/// <param name="args">Arguments to pass to the script</param>
/// <param name="localVars">Local variables passed along with the script</param>
/// <param name="run">Set to false to compile and cache the script without launching it</param>
/// <exception cref="CSharpException">Thrown if an error occured</exception>
/// <returns>Result of the execution, returned by the script</returns>
public static object Run(ChatBot apiHandler, ManualResetEvent tickHandler, string[] lines, string[] args, bool run = true)
public static object Run(ChatBot apiHandler, ManualResetEvent tickHandler, string[] lines, string[] args, Dictionary<string, object> localVars, bool run = true)
{
//Script compatibility check for handling future versions differently
if (lines.Length < 1 || lines[0] != "//MCCScript 1.0")
@ -132,7 +133,7 @@ namespace MinecraftClient
.GetType()
.GetMethod("__run")
.Invoke(compiledScript,
new object[] { new CSharpAPI(apiHandler, tickHandler), args });
new object[] { new CSharpAPI(apiHandler, tickHandler, localVars), args });
}
catch (Exception e) { throw new CSharpException(CSErrorType.RuntimeError, e); }
}
@ -192,15 +193,22 @@ namespace MinecraftClient
/// </summary>
private ManualResetEvent tickHandler;
/// <summary>
/// Holds local variables passed along with the script
/// </summary>
private Dictionary<string, object> localVars;
/// <summary>
/// Create a new C# API Wrapper
/// </summary>
/// <param name="apiHandler">ChatBot API Handler</param>
/// <param name="tickHandler">ChatBot tick handler</param>
public CSharpAPI(ChatBot apiHandler, ManualResetEvent tickHandler)
/// <param name="localVars">Local variables passed along with the script</param>
public CSharpAPI(ChatBot apiHandler, ManualResetEvent tickHandler, Dictionary<string , object> localVars)
{
SetMaster(apiHandler);
this.tickHandler = tickHandler;
this.localVars = localVars;
}
/* == Wrappers for ChatBot API with public visibility and call limit to one per tick for safety == */
@ -231,10 +239,13 @@ namespace MinecraftClient
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
/// </summary>
/// <param name="command">The command to process</param>
/// <param name="localVars">Local variables passed along with the script</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
new public bool PerformInternalCommand(string command)
new public bool PerformInternalCommand(string command, Dictionary<string, object> localVars = null)
{
bool result = base.PerformInternalCommand(command);
if (localVars == null)
localVars = this.localVars;
bool result = base.PerformInternalCommand(command, localVars);
tickHandler.WaitOne();
return result;
}
@ -302,6 +313,8 @@ namespace MinecraftClient
/// <returns>Value of the variable or null if no variable</returns>
public object GetVar(string varName)
{
if (localVars != null && localVars.ContainsKey(varName))
return localVars[varName];
return Settings.GetVar(varName);
}
@ -312,6 +325,8 @@ namespace MinecraftClient
/// <param name="varValue">Value of the variable</param>
public bool SetVar(string varName, object varValue)
{
if (localVars != null && localVars.ContainsKey(varName))
localVars.Remove(varName);
return Settings.SetVar(varName, varValue);
}
@ -385,7 +400,7 @@ namespace MinecraftClient
ChatBots.Script.LookForScript(ref script);
try { lines = File.ReadAllLines(script); }
catch (Exception e) { throw new CSharpException(CSErrorType.FileReadError, e); }
return CSharpRunner.Run(this, tickHandler, lines, args);
return CSharpRunner.Run(this, tickHandler, lines, args, localVars);
}
}
}

View file

@ -138,17 +138,36 @@ namespace MinecraftClient
/// <param name="data">The payload for the message</param>
public virtual void OnPluginMessage(string channel, byte[] data) { }
/// <summary>
/// Called when properties for the Player entity are received from the server
/// </summary>
/// <param name="prop">Dictionary of player properties</param>
public virtual void OnPlayerProperty(Dictionary<string, Double> prop) { }
/// <summary>
/// Called when server TPS are recalculated by MCC based on world time updates
/// </summary>
/// <param name="tps">New estimated server TPS (between 0 and 20)</param>
public virtual void OnServerTpsUpdate(Double tps) { }
/// <summary>
/// Called when an entity moved nearby
/// </summary>
/// <param name="entity">Entity with updated location</param>
public virtual void OnEntityMove(Mapping.Entity entity) { }
/// <summary>
/// Called when an entity spawned nearby
/// </summary>
/// <param name="entity">New Entity</param>
public virtual void OnEntitySpawn(Mapping.Entity entity) { }
/// <summary>
/// Called when an entity despawns/dies nearby
/// </summary>
/// <param name="entity">Entity wich has just disappeared</param>
public virtual void OnEntityDespawn(Mapping.Entity entity) { }
/* =================================================================== */
/* ToolBox - Methods below might be useful while creating your bot. */
/* You should not need to interact with other classes of the program. */
@ -183,11 +202,12 @@ namespace MinecraftClient
/// Perform an internal MCC command (not a server command, use SendText() instead for that!)
/// </summary>
/// <param name="command">The command to process</param>
/// <param name="localVars">Local variables passed along with the command</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
protected bool PerformInternalCommand(string command)
protected bool PerformInternalCommand(string command, Dictionary<string, object> localVars = null)
{
string temp = "";
return Handler.PerformInternalCommand(command, ref temp);
return Handler.PerformInternalCommand(command, ref temp, localVars);
}
/// <summary>
@ -195,10 +215,11 @@ namespace MinecraftClient
/// </summary>
/// <param name="command">The command to process</param>
/// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param>
/// <param name="localVars">Local variables passed along with the command</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
protected bool PerformInternalCommand(string command, ref string response_msg)
protected bool PerformInternalCommand(string command, ref string response_msg, Dictionary<string, object> localVars = null)
{
return Handler.PerformInternalCommand(command, ref response_msg);
return Handler.PerformInternalCommand(command, ref response_msg, localVars);
}
/// <summary>
@ -586,9 +607,10 @@ namespace MinecraftClient
/// </summary>
/// <param name="filename">File name</param>
/// <param name="playername">Player name to send error messages, if applicable</param>
protected void RunScript(string filename, string playername = "")
/// <param name="localVars">Local variables for use in the Script</param>
protected void RunScript(string filename, string playername = null, Dictionary<string, object> localVars = null)
{
Handler.BotLoad(new ChatBots.Script(filename, playername));
Handler.BotLoad(new ChatBots.Script(filename, playername, localVars));
}
/// <summary>
@ -808,15 +830,23 @@ namespace MinecraftClient
return Handler.InteractEntity(EntityID, type);
}
/// <summary>
/// Use item currently in the player's hand (active inventory bar slot)
/// </summary>
/// <returns></returns>
protected bool UseItemOnHand()
{
return Handler.UseItemOnHand();
}
/// <summary>
/// Get a copy of the player's inventory
/// </summary>
/// <returns>Player inventory</returns>
protected Container GetPlayerInventory()
{
Container container = Handler.GetPlayerInventory();
return new Container(container.ID,container.Type,container.Title,container.Items);
return new Container(container.ID, container.Type, container.Title, container.Items);
}
}
}

View file

@ -76,8 +76,9 @@ namespace MinecraftClient.ChatBots
/// <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="msgType">Type of the message public/private message, or other message</param>
/// <param name="localVars">Dictionary to populate with match variables in case of Regex match</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, MessageType msgType)
public string Match(string username, string message, MessageType msgType, Dictionary<string, object> localVars)
{
string toSend = null;
@ -99,9 +100,14 @@ namespace MinecraftClient.ChatBots
if (regex.IsMatch(message))
{
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;
}
}
@ -109,6 +115,8 @@ namespace MinecraftClient.ChatBots
{
if (message.ToLower().Contains(match.ToLower()))
{
localVars["match_0"] = message;
localVars["match_u"] = username;
return toSend.Replace("$u", username);
}
}
@ -225,12 +233,13 @@ namespace MinecraftClient.ChatBots
{
foreach (RespondRule rule in respondRules)
{
string toPerform = rule.Match(sender, message, msgType);
Dictionary<string, object> localVars = new Dictionary<string, object>();
string toPerform = rule.Match(sender, message, msgType, localVars);
if (!String.IsNullOrEmpty(toPerform))
{
string response = null;
LogToConsole(toPerform);
PerformInternalCommand(toPerform, ref response);
PerformInternalCommand(toPerform, ref response, localVars);
if (!String.IsNullOrEmpty(response))
LogToConsole(response);
}

View file

@ -25,17 +25,18 @@ namespace MinecraftClient.ChatBots
private bool csharp;
private Thread thread;
private ManualResetEvent tpause;
private Dictionary<string, object> localVars;
public Script(string filename)
{
ParseArguments(filename);
}
public Script(string filename, string ownername)
public Script(string filename, string ownername, Dictionary<string, object> localVars)
: this(filename)
{
if (ownername != "")
owner = ownername;
this.owner = ownername;
this.localVars = localVars;
}
private void ParseArguments(string argstr)
@ -135,14 +136,14 @@ namespace MinecraftClient.ChatBots
csharp = file.EndsWith(".cs");
thread = null;
if (owner != null)
if (!String.IsNullOrEmpty(owner))
SendPrivateMessage(owner, "Script '" + file + "' loaded.");
}
else
{
LogToConsole("File not found: '" + System.IO.Path.GetFullPath(file) + "'");
if (owner != null)
if (!String.IsNullOrEmpty(owner))
SendPrivateMessage(owner, "File not found: '" + file + "'");
UnloadBot(); //No need to keep the bot active
@ -161,7 +162,7 @@ namespace MinecraftClient.ChatBots
{
try
{
CSharpRunner.Run(this, tpause, lines, args);
CSharpRunner.Run(this, tpause, lines, args, localVars);
}
catch (CSharpException e)
{
@ -198,7 +199,7 @@ namespace MinecraftClient.ChatBots
{
if (instruction_line[0] != '#' && instruction_line[0] != '/' && instruction_line[1] != '/')
{
instruction_line = Settings.ExpandVars(instruction_line);
instruction_line = Settings.ExpandVars(instruction_line, localVars);
string instruction_name = instruction_line.Split(' ')[0];
switch (instruction_name.ToLower())
{

View file

@ -11,40 +11,35 @@ namespace MinecraftClient
/// If inheriting from the 'Command' class and placed in the 'Commands' namespace, the command will be
/// automatically loaded and available in main chat prompt, scripts, remote control and command help.
/// </summary>
public abstract class Command
{
/// <summary>
/// The command name
/// </summary>
public abstract string CMDName { get; }
/// <summary>
/// Usage message, eg: 'name [args]: do something'
/// </summary>
public abstract string CMDDesc { get; }
/// <summary>
/// Perform the command
/// </summary>
/// <param name="command">The full command, eg: 'mycommand arg1 arg2'</param>
/// <param name="localVars">Local variables passed along with the command (may be null)</param>
/// <returns>A confirmation/error message, or "" if no message</returns>
public abstract string Run(McTcpClient handler, string command);
public abstract string Run(McTcpClient handler, string command, Dictionary<string, object> localVars);
/// <summary>
/// Return a list of aliases for this command.
/// Override this method if you wish to put aliases to the command
/// </summary>
public virtual IEnumerable<string> getCMDAliases() { return new string[0]; }
/// <summary>
/// Check if at least one argument has been passed to the command
/// </summary>
public static bool hasArg(string command)
{
int first_space = command.IndexOf(' ');
@ -55,7 +50,6 @@ namespace MinecraftClient
/// Extract the argument string from the command
/// </summary>
/// <returns>Argument or "" if no argument</returns>
public static string getArg(string command)
{
if (hasArg(command))
@ -69,7 +63,6 @@ namespace MinecraftClient
/// Extract the arguments as a string array from the command
/// </summary>
/// <returns>Argument array or empty array if no arguments</returns>
public static string[] getArgs(string command)
{
string[] args = getArg(command).Split(' ');

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "changeslot"; } }
public override string CMDDesc { get { return "changeslot <1-9>: Change hotbar"; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (!handler.GetInventoryEnabled()) return "Please enable InventoryHandling in the config file first.";
if (hasArg(command))

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "connect"; } }
public override string CMDDesc { get { return "connect <server> [account]: connect to the specified server."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (hasArg(command))
{

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "debug"; } }
public override string CMDDesc { get { return "debug [on|off]: toggle debug messages."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (hasArg(command))
{

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "exit"; } }
public override string CMDDesc { get { return "exit: disconnect from the server."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
Program.Exit();
return "";

View file

@ -11,7 +11,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "getinventory"; } }
public override string CMDDesc { get { return "getinventory: Show your inventory."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
Dictionary<int,Item> items = handler.GetPlayerInventory().Items;
foreach(KeyValuePair<int,Item> a in items)

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "list"; } }
public override string CMDDesc { get { return "list: get the player list."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
return "PlayerList: " + String.Join(", ", handler.GetOnlinePlayers());
}

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "log"; } }
public override string CMDDesc { get { return "log <text>: log some text to the console."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (hasArg(command))
{

View file

@ -11,7 +11,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "look"; } }
public override string CMDDesc { get { return "look <x y z|yaw pitch|up|down|east|west|north|south>: look at direction or coordinates."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (handler.GetTerrainEnabled())
{

View file

@ -11,7 +11,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "move"; } }
public override string CMDDesc { get { return "move <on|off|get|up|down|east|west|north|south|x y z>: walk or start walking."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
string[] args = getArgs(command);
string argStr = getArg(command).Trim().ToLower();

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "reco"; } }
public override string CMDDesc { get { return "reco [account]: restart and reconnect to the server."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
string[] args = getArgs(command);
if (args.Length > 0)

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "respawn"; } }
public override string CMDDesc { get { return "respawn: Use this to respawn if you are dead."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
handler.SendRespawnPacket();
return "You have respawned.";

View file

@ -10,11 +10,11 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "script"; } }
public override string CMDDesc { get { return "script <scriptname>: run a script file."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (hasArg(command))
{
handler.BotLoad(new ChatBots.Script(getArg(command)));
handler.BotLoad(new ChatBots.Script(getArg(command), null, localVars));
return "";
}
else return CMDDesc;

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "send"; } }
public override string CMDDesc { get { return "send <text>: send a chat message or command."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (hasArg(command))
{

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "set"; } }
public override string CMDDesc { get { return "set varname=value: set a custom %variable%."; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
if (hasArg(command))
{

View file

@ -10,7 +10,7 @@ namespace MinecraftClient.Commands
public override string CMDName { get { return "useitem"; } }
public override string CMDDesc { get { return "useitem: Use (left click) an item on the hand"; } }
public override string Run(McTcpClient handler, string command)
public override string Run(McTcpClient handler, string command, Dictionary<string, object> localVars)
{
handler.UseItemOnHand();
return "Use an item";

View file

@ -313,8 +313,9 @@ namespace MinecraftClient
/// </summary>
/// <param name="command">The command</param>
/// <param name="response_msg">May contain a confirmation or error message after processing the command, or "" otherwise.</param>
/// <param name="localVars">Local variables passed along with the command</param>
/// <returns>TRUE if the command was indeed an internal MCC command</returns>
public bool PerformInternalCommand(string command, ref string response_msg)
public bool PerformInternalCommand(string command, ref string response_msg, Dictionary<string, object> localVars = null)
{
/* Load commands from the 'Commands' namespace */
@ -363,7 +364,7 @@ namespace MinecraftClient
}
else if (cmds.ContainsKey(command_name))
{
response_msg = cmds[command_name].Run(this, command);
response_msg = cmds[command_name].Run(this, command, localVars);
}
else
{

View file

@ -388,15 +388,15 @@ namespace MinecraftClient
if (command.StartsWith("reco"))
{
message = new Commands.Reco().Run(null, Settings.ExpandVars(command));
message = new Commands.Reco().Run(null, Settings.ExpandVars(command), null);
}
else if (command.StartsWith("connect"))
{
message = new Commands.Connect().Run(null, Settings.ExpandVars(command));
message = new Commands.Connect().Run(null, Settings.ExpandVars(command), null);
}
else if (command.StartsWith("exit") || command.StartsWith("quit"))
{
message = new Commands.Exit().Run(null, Settings.ExpandVars(command));
message = new Commands.Exit().Run(null, Settings.ExpandVars(command), null);
}
else if (command.StartsWith("help"))
{

View file

@ -792,11 +792,12 @@ namespace MinecraftClient
}
/// <summary>
/// Replace %variables% with their value
/// Replace %variables% with their value from global AppVars
/// </summary>
/// <param name="str">String to parse</param>
/// <param name="localContext">Optional local variables overriding global variables</param>
/// <returns>Modifier string</returns>
public static string ExpandVars(string str)
public static string ExpandVars(string str, Dictionary<string, object> localVars = null)
{
StringBuilder result = new StringBuilder();
for (int i = 0; i < str.Length; i++)
@ -808,7 +809,7 @@ namespace MinecraftClient
for (int j = i + 1; j < str.Length; j++)
{
if (!char.IsLetterOrDigit(str[j]))
if (!char.IsLetterOrDigit(str[j]) && str[j] != '_')
{
if (str[j] == '%')
varname_ok = var_name.Length > 0;
@ -829,7 +830,11 @@ namespace MinecraftClient
case "serverip": result.Append(ServerIP); break;
case "serverport": result.Append(ServerPort); break;
default:
if (AppVars.ContainsKey(varname_lower))
if (localVars != null && localVars.ContainsKey(varname_lower))
{
result.Append(localVars[varname_lower].ToString());
}
else if (AppVars.ContainsKey(varname_lower))
{
result.Append(AppVars[varname_lower].ToString());
}

View file

@ -1,4 +1,4 @@
Minecraft Client v1.13.0 for Minecraft 1.4.6 to 1.13.2 - By ORelio & Contributors
Minecraft Console Client User Manual
======
**Thanks for dowloading Minecraft Console Client!**
@ -17,7 +17,7 @@ On Mac or Linux you need to install the Mono Runtime:
- On Linux: sudo apt-get install mono-runtime libmono-reflection-cil
Then, open a terminal in this folder and run "mono MinecraftClient.exe".
If you cannot authenticate on Mono, you'll need to run "mozroots --import --ask-remove" once.
If Mono crashes, retry with mono-complete instead of mono-runtime. Mono v4.0 to 4.2 is recommended.
If Mono crashes, retry with mono-complete instead of mono-runtime. Use at least Mono v4.0.
Using Configuration files & Enabling bots
------
@ -235,7 +235,6 @@ Using the Auto Attack
The AutoAttack bot allows you to automatically attack mobs around you (precisely within radius of 4 block).
To use this bot, you will need to enable **Entity Handling** in the config file first.
**You have been reminded that we will not take any responsible if you got banned by your server while using this bot.**
Using the Auto Fishing
------
@ -249,19 +248,21 @@ Steps for using this bot:
1. Hold a fishing rod and aim towards the sea before login with MCC
2. Make sure AutoFish is enabled in config file
3. Login with MCC
4. Do /useitem and you should see "threw a fishing rod"
5. To stop fishing, do /useitem again
4. Do `/useitem` and you should see "threw a fishing rod"
5. To stop fishing, do `/useitem` again
Disclaimer
------
Even if everything should work, I am not responsible of any damage this app could cause to your computer or your server.
Even if everything should work, We are not responsible for any damage this app could cause to your computer or your server.
This app does not steal your password. If you don't trust it, don't use it or check & compile from the source code.
Also, remember that when you connect to a server with this program, you will appear where you left the last time.
This means that you can die if you log in in an unsafe place on a survival server!
This means that **you can die if you log in in an unsafe place on a survival server!**
Use the script scheduler bot to send a teleport command after logging in.
You have been reminded that **you may get banned** by your server for using this program. Use accordinly with server rules.
License
------
@ -294,8 +295,8 @@ would not have been possible without the help of talented contributors:
**Code contributions:**
Allyoutoo, Aragas, Bancey, bearbear12345, corbanmailloux, dbear20, dogwatch, initsuj,
JamieSinn, justcool393, lokulin, maxpowa, medxo, Pokechu22, repository, TheMeq, TheSnoozer,
vkorn, v1RuX, ZizzyDizzyMC, milutinke
JamieSinn, justcool393, lokulin, maxpowa, medxo, milutinke, Pokechu22, ReinforceZwei,
repository, TheMeq, TheSnoozer, vkorn, v1RuX, yunusemregul, ZizzyDizzyMC
**Libraries:**
@ -324,5 +325,5 @@ Like Minecraft Console Client? You can buy me a coffee here:
Code contributions, bug reports and any kind of comments are also highly appreciated :)
+-----------------------------------+
| © 2012-2019 ORelio & Contributors |
| © 2012-2020 ORelio & Contributors |
+-----------------------------------+

View file

@ -10,6 +10,11 @@
# Regex matches are also supported eg $1, $2, $3.. in actions
# Matches can optionally be restricted to bot owners only
# When running a script from an AutoRespond match,
# additional %variables% are available from within your script:
# %match_u% will hold username of the player triggering the match
# %match_1%, %match_2%, %match_3%.. will hold regex matches
# Simple example: Respond to a message containing a keyword
[Match]