text: "use strict";
  var Brain, inherit_utils, utils;
  
  utils = require("./utils");
  
  inherit_utils = require("./inheritance");
  
  Brain = (function() {
    function Brain(master) {
      this.master = master;
      this.strict = master._strict;
      this.utf8 = master._utf8;
      this.caseSensitive = master._caseSensitive;
      this._currentUser = null;
    }
  
    Brain.prototype.say = function(message) {
      return this.master.say(message);
    };
  
    Brain.prototype.warn = function(message, filename, lineno) {
      return this.master.warn(message, filename, lineno);
    };
  
    Brain.prototype.reply = function(user, msg, scope) {
      var begin, reply;
      this.say("Asked to reply to [" + user + "] " + msg);
      this._currentUser = user;
      msg = this.formatMessage(msg);
      reply = "";
      if (this.master._topics.__begin__) {
        begin = this._getReply(user, "request", "begin", 0, scope);
        if (begin.indexOf("{ok}") > -1) {
          reply = this._getReply(user, msg, "normal", 0, scope);
          begin = begin.replace(/\{ok\}/g, reply);
        }
        reply = begin;
        reply = this.processTags(user, msg, reply, [], [], 0, scope);
      } else {
        reply = this._getReply(user, msg, "normal", 0, scope);
      }
      this.master._users[user].__history__.input.pop();
      this.master._users[user].__history__.input.unshift(msg);
      this.master._users[user].__history__.reply.pop();
      this.master._users[user].__history__.reply.unshift(reply);
      this._currentUser = void 0;
      return reply;
    };
  
    Brain.prototype._getReply = function(user, msg, context, step, scope) {
      var allTopics, botside, bucket, choice, condition, e, eq, foundMatch, giveup, halves, i, isAtomic, isMatch, j, k, l, lastReply, left, len, len1, len2, len3, len4, len5, m, match, matched, matchedTrigger, n, name, nil, o, passed, pattern, potreply, q, r, redirect, ref, ref1, ref2, ref3, ref4, ref5, ref6, regexp, rep, reply, right, row, stars, thatstars, top, topic, trig, userSide, value, weight;
      if (!this.master._sorted.topics) {
        this.warn("You forgot to call sortReplies()!");
        return "ERR: Replies Not Sorted";
      }
      if (!this.master._users[user]) {
        this.master._users[user] = {
          "topic": "random"
        };
      }
      topic = this.master._users[user].topic;
      stars = [];
      thatstars = [];
      reply = "";
      if (!this.master._topics[topic]) {
        this.warn("User " + user + " was in an empty topic named '" + topic + "'");
        topic = this.master._users[user].topic = "random";
      }
      if (step > this.master._depth) {
        return "ERR: Deep Recursion Detected";
      }
      if (context === "begin") {
        topic = "__begin__";
      }
      if (!this.master._users[user].__history__) {
        this.master._users[user].__history__ = {
          "input": ["undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined"],
          "reply": ["undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined", "undefined"]
        };
      }
      if (!this.master._topics[topic]) {
        return "ERR: No default topic 'random' was found!";
      }
      matched = null;
      matchedTrigger = null;
      foundMatch = false;
      if (step === 0) {
        allTopics = [topic];
        if (this.master._topics[topic].includes || this.master._topics[topic].inherits) {
          allTopics = inherit_utils.getTopicTree(this.master, topic);
        }
        for (j = 0, len = allTopics.length; j < len; j++) {
          top = allTopics[j];
          this.say("Checking topic " + top + " for any %Previous's");
          if (this.master._sorted.thats[top]) {
            this.say("There's a %Previous in this topic!");
            lastReply = this.master._users[user].__history__.reply[0];
            lastReply = this.formatMessage(lastReply, true);
            this.say("Last reply: " + lastReply);
            ref = this.master._sorted.thats[top];
            for (k = 0, len1 = ref.length; k < len1; k++) {
              trig = ref[k];
              pattern = trig[0];
              botside = this.triggerRegexp(user, pattern);
              this.say("Try to match lastReply (" + lastReply + ") to " + botside);
              match = lastReply.match(new RegExp("^" + botside + "$", 'i'));
              if (match) {
                this.say("Bot side matched!");
                thatstars = match;
                thatstars.shift();
                userSide = trig[1];
                regexp = this.triggerRegexp(user, userSide.trigger);
                this.say("Try to match \"" + msg + "\" against " + userSide.trigger + " (" + regexp + ")");
                isAtomic = utils.isAtomic(userSide.trigger);
                isMatch = false;
                if (isAtomic) {
                  if (msg === regexp) {
                    isMatch = true;
                  }
                } else {
                  match = msg.match(new RegExp("^" + regexp + "$"));
                  if (match) {
                    isMatch = true;
                    stars = match;
                    if (stars.length >= 1) {
                      stars.shift();
                    }
                  }
                }
                if (isMatch) {
                  matched = userSide;
                  foundMatch = true;
                  matchedTrigger = userSide.trigger;
                  break;
                }
              }
            }
          }
        }
      }
      if (!foundMatch) {
        this.say("Searching their topic for a match...");
        ref1 = this.master._sorted.topics[topic];
        for (l = 0, len2 = ref1.length; l < len2; l++) {
          trig = ref1[l];
          pattern = trig[0];
          regexp = this.triggerRegexp(user, pattern);
          this.say("Try to match \"" + msg + "\" against " + pattern + " (" + regexp + ")");
          isAtomic = utils.isAtomic(pattern);
          isMatch = false;
          if (isAtomic) {
            if (msg === regexp) {
              isMatch = true;
            }
          } else {
            match = msg.match(new RegExp("^" + regexp + "$", 'i'));
            if (match) {
              isMatch = true;
              stars = [];
              if (match.length > 1) {
                for (i = m = 1, ref2 = match.length; 1 <= ref2 ? m <= ref2 : m >= ref2; i = 1 <= ref2 ? ++m : --m) {
                  stars.push(match[i]);
                }
              }
            }
          }
          if (isMatch) {
            this.say("Found a match!");
            matched = trig[1];
            foundMatch = true;
            matchedTrigger = pattern;
            break;
          }
        }
      }
      this.master._users[user].__lastmatch__ = matchedTrigger;
      if (matched) {
        ref3 = [1];
        for (n = 0, len3 = ref3.length; n < len3; n++) {
          nil = ref3[n];
          if (matched.redirect != null) {
            this.say("Redirecting us to " + matched.redirect);
            redirect = this.processTags(user, msg, matched.redirect, stars, thatstars, step, scope);
            this.say("Pretend user said: " + redirect);
            reply = this._getReply(user, redirect, context, step + 1, scope);
            break;
          }
          ref4 = matched.condition;
          for (o = 0, len4 = ref4.length; o < len4; o++) {
            row = ref4[o];
            halves = row.split(/\s*=>\s*/);
            if (halves && halves.length === 2) {
              condition = halves[0].match(/^(.+?)\s+(==|eq|!=|ne|<>|<|<=|>|>=)\s+(.*?)$/);
              if (condition) {
                left = utils.strip(condition[1]);
                eq = condition[2];
                right = utils.strip(condition[3]);
                potreply = utils.strip(halves[1]);
                left = this.processTags(user, msg, left, stars, thatstars, step, scope);
                right = this.processTags(user, msg, right, stars, thatstars, step, scope);
                if (left.length === 0) {
                  left = "undefined";
                }
                if (right.length === 0) {
                  right = "undefined";
                }
                this.say("Check if " + left + " " + eq + " " + right);
                passed = false;
                if (eq === "eq" || eq === "==") {
                  if (left === right) {
                    passed = true;
                  }
                } else if (eq === "ne" || eq === "!=" || eq === "<>") {
                  if (left !== right) {
                    passed = true;
                  }
                } else {
                  try {
                    left = parseInt(left);
                    right = parseInt(right);
                    if (eq === "<" && left < right) {
                      passed = true;
                    } else if (eq === "<=" && left <= right) {
                      passed = true;
                    } else if (eq === ">" && left > right) {
                      passed = true;
                    } else if (eq === ">=" && left >= right) {
                      passed = true;
                    }
                  } catch (_error) {
                    e = _error;
                    this.warn("Failed to evaluate numeric condition!");
                  }
                }
                if (passed) {
                  reply = potreply;
                  break;
                }
              }
            }
          }
          if (reply !== void 0 && reply.length > 0) {
            break;
          }
          bucket = [];
          ref5 = matched.reply;
          for (q = 0, len5 = ref5.length; q < len5; q++) {
            rep = ref5[q];
            weight = 1;
            match = rep.match(/\{weight=(\d+?)\}/i);
            if (match) {
              weight = match[1];
              if (weight <= 0) {
                this.warn("Can't have a weight <= 0!");
                weight = 1;
              }
            }
            for (i = r = 0, ref6 = weight; 0 <= ref6 ? r <= ref6 : r >= ref6; i = 0 <= ref6 ? ++r : --r) {
              bucket.push(rep);
            }
          }
          choice = parseInt(Math.random() * bucket.length);
          reply = bucket[choice];
          break;
        }
      }
      if (!foundMatch) {
        reply = "ERR: No Reply Matched";
      } else if (reply === void 0 || reply.length === 0) {
        reply = "ERR: No Reply Found";
      }
      this.say("Reply: " + reply);
      if (context === "begin") {
        match = reply.match(/\{topic=(.+?)\}/i);
        giveup = 0;
        while (match) {
          giveup++;
          if (giveup >= 50) {
            this.warn("Infinite loop looking for topic tag!");
            break;
          }
          name = match[1];
          this.master._users[user].topic = name;
          reply = reply.replace(new RegExp("{topic=" + utils.quotemeta(name) + "}", "ig"), "");
          match = reply.match(/\{topic=(.+?)\}/i);
        }
        match = reply.match(/<set (.+?)=(.+?)>/i);
        giveup = 0;
        while (match) {
          giveup++;
          if (giveup >= 50) {
            this.warn("Infinite loop looking for set tag!");
            break;
          }
          name = match[1];
          value = match[2];
          this.master._users[user][name] = value;
          reply = reply.replace(new RegExp("<set " + utils.quotemeta(name) + "=" + utils.quotemeta(value) + ">", "ig"), "");
          match = reply.match(/<set (.+?)=(.+?)>/i);
        }
      } else {
        reply = this.processTags(user, msg, reply, stars, thatstars, step, scope);
      }
      return reply;
    };
  
    Brain.prototype.formatMessage = function(msg, botreply) {
      msg = "" + msg;
      if (this.caseSensitive !== true) {
        msg = msg.toLowerCase();
      }
      msg = this.substitute(msg, "sub");
      if (this.utf8) {
        msg = msg.replace(/[\\<>]+/, "");
        if (this.master.unicodePunctuation != null) {
          msg = msg.replace(this.master.unicodePunctuation, "");
        }
        if (botreply != null) {
          msg = msg.replace(/[.?,!;:@#$%^&*()]/, "");
        }
      } else {
        msg = utils.stripNasties(msg, this.utf8);
      }
      return msg;
    };
  
    Brain.prototype.triggerRegexp = function(user, regexp) {
      var giveup, i, j, k, l, len, len1, match, name, opts, p, parts, pipes, ref, rep, type;
      regexp = regexp.replace(/^\*$/, "<zerowidthstar>");
      regexp = regexp.replace(/\*/g, "(.+?)");
      regexp = regexp.replace(/#/g, "(\\d+?)");
      regexp = regexp.replace(/_/g, "(\\w+?)");
      regexp = regexp.replace(/\{weight=\d+\}/g, "");
      regexp = regexp.replace(/<zerowidthstar>/g, "(.*?)");
      if (this.utf8) {
        regexp = regexp.replace(/\\@/, "\\u0040");
      }
      match = regexp.match(/\[(.+?)\]/);
      giveup = 0;
      while (match) {
        if (giveup++ > 50) {
          this.warn("Infinite loop when trying to process optionals in a trigger!");
          return "";
        }
        parts = match[1].split("|");
        opts = [];
        for (j = 0, len = parts.length; j < len; j++) {
          p = parts[j];
          opts.push("(?:\\s|\\b)+" + p + "(?:\\s|\\b)+");
        }
        pipes = opts.join("|");
        pipes = pipes.replace(new RegExp(utils.quotemeta("(.+?)"), "g"), "(?:.+?)");
        pipes = pipes.replace(new RegExp(utils.quotemeta("(\\d+?)"), "g"), "(?:\\d+?)");
        pipes = pipes.replace(new RegExp(utils.quotemeta("(\\w+?)"), "g"), "(?:\\w+?)");
        pipes = pipes.replace(/\[/g, "__lb__").replace(/\]/g, "__rb__");
        regexp = regexp.replace(new RegExp("\\s*\\[" + utils.quotemeta(match[1]) + "\\]\\s*"), "(?:" + pipes + "|(?:\\b|\\s)+)");
        match = regexp.match(/\[(.+?)\]/);
      }
      regexp = regexp.replace(/__lb__/g, "[").replace(/__rb__/g, "]");
      regexp = regexp.replace(/\\w/, "[A-Za-z]");
      giveup = 0;
      while (regexp.indexOf("@") > -1) {
        if (giveup++ > 50) {
          break;
        }
        match = regexp.match(/\@(.+?)\b/);
        if (match) {
          name = match[1];
          rep = "";
          if (this.master._array[name]) {
            rep = "(?:" + this.master._array[name].join("|") + ")";
          }
          regexp = regexp.replace(new RegExp("@" + utils.quotemeta(name) + "\\b"), rep);
        }
      }
      giveup = 0;
      while (regexp.indexOf("<bot") > -1) {
        if (giveup++ > 50) {
          break;
        }
        match = regexp.match(/<bot (.+?)>/i);
        if (match) {
          name = match[1];
          rep = '';
          if (this.master._var[name]) {
            rep = utils.stripNasties(this.master._var[name]);
          }
          regexp = regexp.replace(new RegExp("<bot " + utils.quotemeta(name) + ">"), rep.toLowerCase());
        }
      }
      giveup = 0;
      while (regexp.indexOf("<get") > -1) {
        if (giveup++ > 50) {
          break;
        }
        match = regexp.match(/<get (.+?)>/i);
        if (match) {
          name = match[1];
          rep = 'undefined';
          if (typeof this.master._users[user][name] !== "undefined") {
            rep = this.master._users[user][name];
          }
          regexp = regexp.replace(new RegExp("<get " + utils.quotemeta(name) + ">", "ig"), rep.toLowerCase());
        }
      }
      giveup = 0;
      regexp = regexp.replace(/<input>/i, "<input1>");
      regexp = regexp.replace(/<reply>/i, "<reply1>");
      while (regexp.indexOf("<input") > -1 || regexp.indexOf("<reply") > -1) {
        if (giveup++ > 50) {
          break;
        }
        ref = ["input", "reply"];
        for (k = 0, len1 = ref.length; k < len1; k++) {
          type = ref[k];
          for (i = l = 1; l <= 9; i = ++l) {
            if (regexp.indexOf("<" + type + i + ">") > -1) {
              regexp = regexp.replace(new RegExp("<" + type + i + ">", "g"), this.master._users[user].__history__[type][i]);
            }
          }
        }
      }
      if (this.utf8 && regexp.indexOf("\\u") > -1) {
        regexp = regexp.replace(/\\u([A-Fa-f0-9]{4})/, function(match, grp) {
          return String.fromCharCode(parseInt(grp, 16));
        });
      }
      return regexp;
    };
  
    Brain.prototype.processTags = function(user, msg, reply, st, bst, step, scope) {
      var botstars, content, data, formats, giveup, i, insert, j, k, l, len, m, match, name, output, parts, random, ref, ref1, replace, result, stars, subreply, tag, target, text, type, value;
      stars = [""];
      stars.push.apply(stars, st);
      botstars = [""];
      botstars.push.apply(botstars, bst);
      if (stars.length === 1) {
        stars.push("undefined");
      }
      if (botstars.length === 1) {
        botstars.push("undefined");
      }
      reply = reply.replace(/<person>/ig, "{person}<star>{/person}");
      reply = reply.replace(/<@>/ig, "{@<star>}");
      reply = reply.replace(/<formal>/ig, "{formal}<star>{/formal}");
      reply = reply.replace(/<sentence>/ig, "{sentence}<star>{/sentence}");
      reply = reply.replace(/<uppercase>/ig, "{uppercase}<star>{/uppercase}");
      reply = reply.replace(/<lowercase>/ig, "{lowercase}<star>{/lowercase}");
      reply = reply.replace(/\{weight=\d+\}/ig, "");
      reply = reply.replace(/<star>/ig, stars[1]);
      reply = reply.replace(/<botstar>/ig, botstars[1]);
      for (i = j = 1, ref = stars.length; 1 <= ref ? j <= ref : j >= ref; i = 1 <= ref ? ++j : --j) {
        reply = reply.replace(new RegExp("<star" + i + ">", "ig"), stars[i]);
      }
      for (i = k = 1, ref1 = botstars.length; 1 <= ref1 ? k <= ref1 : k >= ref1; i = 1 <= ref1 ? ++k : --k) {
        reply = reply.replace(new RegExp("<botstar" + i + ">", "ig"), botstars[i]);
      }
      reply = reply.replace(/<input>/ig, this.master._users[user].__history__.input[0]);
      reply = reply.replace(/<reply>/ig, this.master._users[user].__history__.reply[0]);
      for (i = l = 1; l <= 9; i = ++l) {
        if (reply.indexOf("<input" + i + ">") > -1) {
          reply = reply.replace(new RegExp("<input" + i + ">", "ig"), this.master._users[user].__history__.input[i]);
        }
        if (reply.indexOf("<reply" + i + ">") > -1) {
          reply = reply.replace(new RegExp("<reply" + i + ">", "ig"), this.master._users[user].__history__.reply[i]);
        }
      }
      reply = reply.replace(/<id>/ig, user);
      reply = reply.replace(/\\s/ig, " ");
      reply = reply.replace(/\\n/ig, "\n");
      reply = reply.replace(/\\#/ig, "#");
      match = reply.match(/\{random\}(.+?)\{\/random\}/i);
      giveup = 0;
      while (match) {
        if (giveup++ > this.master._depth) {
          this.warn("Infinite loop looking for random tag!");
          break;
        }
        random = [];
        text = match[1];
        if (text.indexOf("|") > -1) {
          random = text.split("|");
        } else {
          random = text.split(" ");
        }
        output = random[parseInt(Math.random() * random.length)];
        reply = reply.replace(new RegExp("\\{random\\}" + utils.quotemeta(text) + "\\{\\/random\\}", "ig"), output);
        match = reply.match(/\{random\}(.+?)\{\/random\}/i);
      }
      formats = ["person", "formal", "sentence", "uppercase", "lowercase"];
      for (m = 0, len = formats.length; m < len; m++) {
        type = formats[m];
        match = reply.match(new RegExp("{" + type + "}(.+?){/" + type + "}", "i"));
        giveup = 0;
        while (match) {
          giveup++;
          if (giveup >= 50) {
            this.warn("Infinite loop looking for " + type + " tag!");
            break;
          }
          content = match[1];
          if (type === "person") {
            replace = this.substitute(content, "person");
          } else {
            replace = utils.stringFormat(type, content);
          }
          reply = reply.replace(new RegExp(("{" + type + "}") + utils.quotemeta(content) + ("{/" + type + "}"), "ig"), replace);
          match = reply.match(new RegExp("{" + type + "}(.+?){/" + type + "}", "i"));
        }
      }
      reply = reply.replace(/<call>/ig, "{__call__}");
      reply = reply.replace(/<\/call>/ig, "{/__call__}");
      while (true) {
        match = reply.match(/<([^<]+?)>/);
        if (!match) {
          break;
        }
        match = match[1];
        parts = match.split(" ");
        tag = parts[0].toLowerCase();
        data = "";
        if (parts.length > 1) {
          data = parts.slice(1).join(" ");
        }
        insert = "";
        if (tag === "bot" || tag === "env") {
          target = tag === "bot" ? this.master._var : this.master._global;
          if (data.indexOf("=") > -1) {
            parts = data.split("=", 2);
            this.say("Set " + tag + " variable " + parts[0] + " = " + parts[1]);
            target[parts[0]] = parts[1];
          } else {
            insert = target[data] || "undefined";
          }
        } else if (tag === "set") {
          parts = data.split("=", 2);
          this.say("Set uservar " + parts[0] + " = " + parts[1]);
          this.master._users[user][parts[0]] = parts[1];
        } else if (tag === "add" || tag === "sub" || tag === "mult" || tag === "div") {
          parts = data.split("=");
          name = parts[0];
          value = parts[1];
          if (typeof this.master._users[user][name] !== "undefined") {
            this.master._users[user][name] = 0;
          }
          value = parseInt(value);
          if (isNaN(value)) {
            insert = "[ERR: Math can't '" + tag + "' non-numeric value '" + value + "']";
          } else if (isNaN(parseInt(this.master._users[user][name]))) {
            insert = "[ERR: Math can't '" + tag + "' non-numeric user variable '" + name + "']";
          } else {
            result = parseInt(this.master._users[user][name]);
            if (tag === "add") {
              result += value;
            } else if (tag === "sub") {
              result -= value;
            } else if (tag === "mult") {
              result *= value;
            } else if (tag === "div") {
              if (value === 0) {
                insert = "[ERR: Can't Divide By Zero]";
              } else {
                result /= value;
              }
            }
            if (insert === "") {
              this.master._users[user][name] = result;
            }
          }
        } else if (tag === "get") {
          insert = typeof (this.master._users[user][data] !== "undefined") ? this.master._users[user][data] : "undefined";
        } else {
          insert = "\x00" + match + "\x01";
        }
        reply = reply.replace(new RegExp("<" + (utils.quotemeta(match)) + ">"), insert);
      }
      reply = reply.replace(/\x00/g, "<");
      reply = reply.replace(/\x01/g, ">");
      match = reply.match(/\{topic=(.+?)\}/i);
      giveup = 0;
      while (match) {
        giveup++;
        if (giveup >= 50) {
          this.warn("Infinite loop looking for topic tag!");
          break;
        }
        name = match[1];
        this.master._users[user].topic = name;
        reply = reply.replace(new RegExp("{topic=" + utils.quotemeta(name) + "}", "ig"), "");
        match = reply.match(/\{topic=(.+?)\}/i);
      }
      match = reply.match(/\{@(.+?)\}/);
      giveup = 0;
      while (match) {
        giveup++;
        if (giveup >= 50) {
          this.warn("Infinite loop looking for redirect tag!");
          break;
        }
        target = match[1];
        this.say("Inline redirection to: " + target);
        subreply = this._getReply(user, target, "normal", step + 1, scope);
        reply = reply.replace(new RegExp("\\{@" + utils.quotemeta(target) + "\\}", "i"), subreply);
        match = reply.match(/\{@(.+?)\}/);
      }
      reply = reply.replace(/\{__call__\}/g, "<call>");
      reply = reply.replace(/\{\/__call__\}/g, "</call>");
      match = reply.match(/<call>(.+?)<\/call>/i);
      giveup = 0;
      return this.processCalls(giveup, match, reply, scope);
    };
  
    Brain.prototype.processCalls = function(giveup, match, reply, scope) {
      var args, lang, obj, output, parts, text;
      if (!match) {
        return reply;
      }
      giveup++;
      if (giveup >= 50) {
        this.warn("Infinite loop looking for call tag!");
        return reply;
      }
      text = utils.strip(match[1]);
      parts = text.split(/\s+/);
      obj = parts.shift();
      args = parts;
      output = "";
      if (this.master._objlangs[obj]) {
        lang = this.master._objlangs[obj];
        if (this.master._handlers[lang]) {
          output = this.master._handlers[lang].call(this.master, obj, args, scope);
        } else {
          output = "[ERR: No Object Handler]";
        }
      } else {
        output = "[ERR: Object Not Found]";
      }
      if (output.then) {
        return this.asyncCallReply(output, reply, match, giveup);
      } else {
        reply = reply.replace(new RegExp("<call>" + utils.quotemeta(match[1]) + "</call>", "i"), output);
        match = reply.match(/<call>(.+?)<\/call>/i);
        return this.processCalls(giveup, match, reply);
      }
    };
  
    Brain.prototype.asyncCallReply = function(outputP, reply, match, giveup, scope) {
      return outputP.then((function(_this) {
        return function(output) {
          reply = reply.replace(new RegExp("<call>" + utils.quotemeta(match[1]) + "</call>", "i"), output);
          match = reply.match(/<call>(.+?)<\/call>/i);
          return _this.processCalls(giveup, match, reply, scope);
        };
      })(this));
    };
  
    Brain.prototype.substitute = function(msg, type) {
      var cap, j, len, match, pattern, ph, pi, placeholder, qm, ref, result, subs, tries;
      result = "";
      if (!this.master._sorted[type]) {
        this.master.warn("You forgot to call sortReplies()!");
        return "";
      }
      subs = type === "sub" ? this.master._sub : this.master._person;
      ph = [];
      pi = 0;
      ref = this.master._sorted[type];
      for (j = 0, len = ref.length; j < len; j++) {
        pattern = ref[j];
        result = subs[pattern];
        qm = utils.quotemeta(pattern);
        ph.push(result);
        placeholder = "\x00" + pi + "\x00";
        pi++;
        msg = msg.replace(new RegExp("^" + qm + "$", "g"), placeholder);
        msg = msg.replace(new RegExp("^" + qm + "(\\W+)", "g"), placeholder + "$1");
        msg = msg.replace(new RegExp("(\\W+)" + qm + "(\\W+)", "g"), "$1" + placeholder + "$2");
        msg = msg.replace(new RegExp("(\\W+)" + qm + "$", "g"), "$1" + placeholder);
      }
      tries = 0;
      while (msg.indexOf("\x00") > -1) {
        tries++;
        if (tries > 50) {
          this.warn("Too many loops in substitution placeholders!");
          break;
        }
        match = msg.match("\\x00(.+?)\\x00");
        if (match) {
          cap = parseInt(match[1]);
          result = ph[cap];
          msg = msg.replace(new RegExp("\x00" + cap + "\x00", "g"), result);
        }
      }
      return msg;
    };
  
    return Brain;
  
  })();
  
  module.exports = Brain;
  