/// <summary> /// Formats a trigger for the regular expression engine. /// </summary> /// <param name="user">The user ID of the caller.</param> /// <param name="profile">Client profile</param> /// <param name="trigger">The raw trigger text.</param> /// <returns></returns> private string triggerRegexp(string user, Client profile, string trigger) { // If the trigger is simply '*', it needs to become (.*?) so it catches the empty string. var regexp = trigger.ReplaceRegex("^\\*$", "<zerowidthstar>"); // Simple regexps are simple. regexp = regexp.ReplaceRegex("\\*", "(.+?)"); // * -> (.+?) regexp = regexp.ReplaceRegex("#", "(\\d+?)"); // # -> (\d+?) regexp = regexp.ReplaceRegex("_", "(\\w+?)"); // _ -> ([A-Za-z ]+?) regexp = regexp.ReplaceRegex("\\{weight=\\d+\\}", ""); // Remove {weight} tags regexp = regexp.ReplaceRegex("<zerowidthstar>", "(.*?)"); // * -> (.*?) // Handle optionals. if (regexp.IndexOf("[") > -1) { Regex reOpts = new Regex("\\s*\\[(.+?)\\]\\s*"); foreach (Match mOpts in reOpts.Matches(regexp)) { var optional = mOpts.Groups[0].Value; var contents = mOpts.Groups[1].Value; // Split them at the pipes. string[] parts = contents.SplitRegex("\\|"); // Construct a regexp part. StringBuilder re = new StringBuilder(); for (int i = 0; i < parts.Length; i++) { // We want: \s*part\s* re.Append("\\s*" + parts[i] + "\\s*"); if (i < parts.Length - 1) { re.Append("|"); } } string pipes = re.ToString(); // If this optional had a star or anything in it, e.g. [*], // make it non-matching. pipes = pipes.ReplaceRegex("\\(.+?\\)", "(?:.+?)"); pipes = pipes.ReplaceRegex("\\(\\d+?\\)", "(?:\\\\d+?)"); pipes = pipes.ReplaceRegex("\\(\\w+?\\)", "(?:\\\\w+?)"); // Put the new text in. pipes = "(?:" + pipes + "|\\s*)"; regexp = regexp.Replace(optional, pipes); } } // Make \w more accurate for our purposes. regexp = regexp.Replace("\\w", "[A-Za-z ]"); // Filter in arrays. if (regexp.IndexOf("@") > -1) { // Match the array's name. Regex reArray = new Regex("\\@(.+?)\\b"); foreach (Match mArray in reArray.Matches(regexp)) { string array = mArray.Groups[0].Value; string name = mArray.Groups[1].Value; // Do we have an array by this name? if (arrays.ContainsKey(name)) { string[] values = arrays[name].ToArray(); StringBuilder joined = new StringBuilder(); // Join the array. for (int i = 0; i < values.Length; i++) { joined.Append(values[i]); if (i < values.Length - 1) { joined.Append("|"); } } // Final contents... string rep = "(?:" + joined.ToString() + ")"; regexp = regexp.Replace(array, rep); } else { // No array by this name. regexp = regexp.Replace(array, ""); } } } // Filter in bot variables. if (regexp.IndexOf("<bot") > -1) { Regex reBot = new Regex("<bot (.+?)>"); foreach (Match mBot in reBot.Matches(regexp)) { string tag = mBot.Groups[0].Value; string var = mBot.Groups[1].Value; //string value = vars[var].ToLower().ReplaceRegex("[^a-z0-9 ]+", ""); string value = Util.StripNasties(vars[var].ToLower(), utf8); // Have this? if (vars.ContainsKey(var)) { regexp = regexp.Replace(tag, value); } else { regexp = regexp.Replace(tag, "undefined"); } } } // Filter in user variables. if (regexp.IndexOf("<get") > -1) { Regex reGet = new Regex("<get (.+?)>"); foreach (Match mGet in reGet.Matches(regexp)) { string tag = mGet.Groups[0].Value; string var = mGet.Groups[1].Value; //string value = profile.get(var).ToLower().ReplaceRegex("[^a-z0-9 ]+", ""); string value = Util.StripNasties(profile.get(var).ToLower(), utf8); // Have this? regexp = regexp.Replace(tag, value); } } // Input and reply tags. regexp = regexp.ReplaceRegex("<input>", "<input1>"); regexp = regexp.ReplaceRegex("<reply>", "<reply1>"); if (regexp.IndexOf("<input") > -1) { Regex reInput = new Regex("<input([0-9])>"); foreach (Match mInput in reInput.Matches(regexp)) { string tag = mInput.Groups[0].Value; int index = int.Parse(mInput.Groups[1].Value); //string text = profile.getInput(index).ToLower().ReplaceRegex("[^a-z0-9 ]+", ""); string text = Util.StripNasties(profile.getInput(index).ToLower(), utf8); regexp = regexp.Replace(tag, text); } } if (regexp.IndexOf("<reply") > -1) { Regex reReply = new Regex("<reply([0-9])>"); foreach (Match mReply in reReply.Matches(regexp)) { string tag = mReply.Groups[0].Value; int index = int.Parse(mReply.Groups[1].Value); //string text = profile.getReply(index).ToLower().ReplaceRegex("[^a-z0-9 ]+", ""); string text = Util.StripNasties(profile.getReply(index).ToLower(), utf8); regexp = regexp.Replace(tag, text); } } return regexp; }
/// <summary> /// Process reply tags. /// </summary> /// <param name="user">The name of the end user.</param> /// <param name="profile">The RiveScript client object holding the user's profile</param> /// <param name="message">The message sent by the user.</param> /// <param name="reply">The bot's original reply including tags.</param> /// <param name="vstars"> The vector of wildcards the user's message matched.</param> /// <param name="vbotstars">The vector of wildcards in any %Previous.</param> /// <param name="step">The current recursion depth limit.</param> /// <returns></returns> private string processTags(string user, Client profile, string message, string reply, List<string> vstars, List<string> vbotstars, int step) { // Pad the stars. vstars.Insert(0, ""); vbotstars.Insert(0, ""); // Set a default first star. if (vstars.Count == 1) { vstars.Add("undefined"); } if (vbotstars.Count == 1) { vbotstars.Add("undefined"); } // Convert the stars into simple arrays. string[] stars = vstars.ToArray(); string[] botstars = vbotstars.ToArray(); // Shortcut tags. reply = reply.ReplaceRegex("<person>", "{person}<star>{/person}"); reply = reply.ReplaceRegex("<@>", "{@<star>}"); reply = reply.ReplaceRegex("<formal>", "{formal}<star>{/formal}"); reply = reply.ReplaceRegex("<sentence>", "{sentence}<star>{/sentence}"); reply = reply.ReplaceRegex("<uppercase>", "{uppercase}<star>{/uppercase}"); reply = reply.ReplaceRegex("<lowercase>", "{lowercase}<star>{/lowercase}"); // Quick tags. reply = reply.ReplaceRegex("\\{weight=\\d+\\}", ""); // Remove {weight}s reply = reply.ReplaceRegex("<input>", "<input1>"); reply = reply.ReplaceRegex("<reply>", "<reply1>"); reply = reply.ReplaceRegex("<id>", user); reply = reply.ReplaceRegex("\\\\s", " "); reply = reply.ReplaceRegex("\\\\n", "\n"); reply = reply.ReplaceRegex("\\\\", "\\"); reply = reply.ReplaceRegex("\\#", "#"); // Stars reply = reply.ReplaceRegex("<star>", stars[1]); reply = reply.ReplaceRegex("<botstar>", botstars[1]); for (int i = 1; i < stars.Length; i++) { reply = reply.ReplaceRegex("<star" + i + ">", stars[i]); } for (int i = 1; i < botstars.Length; i++) { reply = reply.ReplaceRegex("<botstar" + i + ">", botstars[i]); } reply = reply.ReplaceRegex("<(star|botstar)\\d+>", ""); // Input and reply tags. if (reply.IndexOf("<input") > -1) { Regex reInput = new Regex("<input([0-9])>"); foreach (Match mInput in reInput.Matches(reply)) { string tag = mInput.Groups[0].Value; int index = int.Parse(mInput.Groups[1].Value); //string text = profile.getInput(index).ToLower().ReplaceRegex("[^a-z0-9 ]+", ""); string text = Util.StripNasties(profile.getInput(index).ToLower(), utf8); reply = reply.Replace(tag, text); } } if (reply.IndexOf("<reply") > -1) { Regex reReply = new Regex("<reply([0-9])>"); foreach (Match mReply in reReply.Matches(reply)) { string tag = mReply.Groups[0].Value; int index = int.Parse(mReply.Groups[1].Value); //string text = profile.getReply(index).ToLower().ReplaceRegex("[^a-z0-9 ]+", ""); string text = Util.StripNasties(profile.getReply(index).ToLower(), utf8); reply = reply.Replace(tag, text); } } // {random} tag if (reply.IndexOf("{random}") > -1) { Regex reRandom = new Regex("\\{random\\}(.+?)\\{\\/random\\}"); foreach (Match mRandom in reRandom.Matches(reply)) { string tag = mRandom.Groups[0].Value; string[] candidates = mRandom.Groups[1].Value.SplitRegex("\\|"); string chosen = candidates[rand.Next(candidates.Length)]; reply = reply.Replace(tag, chosen); } } // <bot> tag if (reply.IndexOf("<bot") > -1) { Regex reBot = new Regex("<bot (.+?)>"); foreach (Match mBot in reBot.Matches(reply)) { string tag = mBot.Groups[0].Value; string var = mBot.Groups[1].Value; // Have this? if (vars.ContainsKey(var)) { reply = reply.Replace(tag, vars[var]); } else { reply = reply.Replace(tag, "undefined"); } } } // <env> tag if (reply.IndexOf("<env") > -1) { Regex reEnv = new Regex("<env (.+?)>"); foreach (Match mEnv in reEnv.Matches(reply)) { string tag = mEnv.Groups[0].Value; string var = mEnv.Groups[1].Value; // Have this? if (globals.ContainsKey(var)) { reply = reply.Replace(tag, globals[var]); } else { reply = reply.Replace(tag, "undefined"); } } } // {!stream} tag if (reply.IndexOf("{!") > -1) { Regex reStream = new Regex("\\{\\!(.+?)\\}"); foreach (Match mStream in reStream.Matches(reply)) { string tag = mStream.Groups[0].Value; string code = mStream.Groups[1].Value; say("Stream new code in: " + code); // Stream it. this.stream(code); reply = reply.Replace(tag, ""); } } // {person} if (reply.IndexOf("{person}") > -1) { Regex rePerson = new Regex("\\{person\\}(.+?)\\{\\/person\\}"); foreach (Match mPerson in rePerson.Matches(reply)) { string tag = mPerson.Groups[0].Value; string text = mPerson.Groups[1].Value; // Run person substitutions. say("Run person substitutions: before: " + text); text = Util.Substitute(person_s, person, text); say("After: " + text); reply = reply.Replace(tag, text); } } // {formal,uppercase,lowercase,sentence} tags if (reply.IndexOf("{formal}") > -1 || reply.IndexOf("{sentence}") > -1 || reply.IndexOf("{uppercase}") > -1 || reply.IndexOf("{lowercase}") > -1) { string[] tags = { "formal", "sentence", "uppercase", "lowercase" }; for (int i = 0; i < tags.Length; i++) { Regex reTag = new Regex("\\{" + tags[i] + "\\}(.+?)\\{\\/" + tags[i] + "\\}"); foreach (Match mTag in reTag.Matches(reply)) { string tag = mTag.Groups[0].Value; string text = mTag.Groups[1].Value; // string transform. text = stringTransform(tags[i], text); reply = reply.Replace(tag, text); } } } // <set> tag if (reply.IndexOf("<set") > -1) { Regex reSet = new Regex("<set (.+?)=(.+?)>"); foreach (Match mSet in reSet.Matches(reply)) { string tag = mSet.Groups[0].Value; string var = mSet.Groups[1].Value; string value = mSet.Groups[2].Value; // Set the uservar. profile.set(var, value); reply = reply.Replace(tag, ""); say("Set user var " + var + "=" + value); } } // <add, sub, mult, div> tags if (reply.IndexOf("<add") > -1 || reply.IndexOf("<sub") > -1 || reply.IndexOf("<mult") > -1 || reply.IndexOf("<div") > -1) { string[] tags = { "add", "sub", "mult", "div" }; for (int i = 0; i < tags.Length; i++) { Regex reTag = new Regex("<" + tags[i] + " (.+?)=(.+?)>"); foreach (Match mTag in reTag.Matches(reply)) { string tag = mTag.Groups[0].Value; string var = mTag.Groups[1].Value; string value = mTag.Groups[2].Value; // Get the user var. string curvalue = profile.get(var); int current = 0; if (!curvalue.Equals("undefined")) { // Convert it to a int. try { current = int.Parse(curvalue); } catch (FormatException) { // Current value isn't a number! reply = reply.Replace(tag, "[ERR: Can't \"" + tags[i] + "\" non-numeric variable " + var + "]"); continue; } } // Value must be a number too. int modifier = 0; try { modifier = int.Parse(value); } catch (FormatException) { reply = reply.Replace(tag, "[ERR: Can't \"" + tags[i] + "\" non-numeric value " + value + "]"); continue; } // Run the operation. if (tags[i].Equals("add")) { current += modifier; } else if (tags[i].Equals("sub")) { current -= modifier; } else if (tags[i].Equals("mult")) { current *= modifier; } else { // Don't divide by zero. if (modifier == 0) { reply = reply.Replace(tag, "[ERR: Can't divide by zero!]"); continue; } current /= modifier; } // Store the new value. profile.set(var, current.ToString()); reply = reply.Replace(tag, ""); } } } // <get> tag if (reply.IndexOf("<get") > -1) { Regex reGet = new Regex("<get (.+?)>"); foreach (Match mGet in reGet.Matches(reply)) { string tag = mGet.Groups[0].Value; string var = mGet.Groups[1].Value; // Get the user var. reply = reply.Replace(tag, profile.get(var)); } } // {topic} tag if (reply.IndexOf("{topic=") > -1) { Regex reTopic = new Regex("\\{topic=(.+?)\\}"); foreach (Match mTopic in reTopic.Matches(reply)) { string tag = mTopic.Groups[0].Value; string topic = mTopic.Groups[1].Value; say("Set user's topic to: " + topic); profile.set("topic", topic); reply = reply.Replace(tag, ""); } } // {@redirect} tag if (reply.IndexOf("{@") > -1) { Regex reRed = new Regex("\\{@(.+?)\\}"); foreach (Match mRed in reRed.Matches(reply)) { string tag = mRed.Groups[0].Value; string target = mRed.Groups[1].Value.Trim(); // Do the reply redirect. string subreply = this.reply(user, target, false, step + 1); reply = reply.Replace(tag, subreply); } } // <call> tag if (reply.IndexOf("<call>") > -1) { Regex reCall = new Regex("<call>(.+?)<\\/call>"); foreach (Match mCall in reCall.Matches(reply)) { string tag = mCall.Groups[0].Value; string data = mCall.Groups[1].Value; string[] parts = data.Split(" "); string name = parts[0]; List<String> args = new List<String>(); for (int i = 1; i < parts.Length; i++) { args.Add(parts[i]); } // See if we know of this object. if (objects.ContainsKey(name)) { // What language handles it? string lang = objects[name]; string result = handlers[lang].onCall(name, this, args.ToArray()); reply = reply.Replace(tag, result); } else { reply = reply.Replace(tag, "[ERR: Object Not Found]"); } } } return reply; }