Source: adapters/ApiAiAdapter.js

/**
 * A basic unofficial compatibility object that can read {@link http://api.ai|Api.ai} exported packages and simulates its output.
 * @constructor
 * @param {string} packagePath - The path to the exported Api.ai package root.
 * @param {string} extensions.language - The language to be used. Possible values are namespace names of {@link Bravey.Language}. 
 * @param {string} extensions.nlp - The NLP processor to be used. Possible values are namespace names of {@link Bravey.Nlp}. 
 */
Bravey.ApiAiAdapter = function(packagePath, extensions) {
  extensions = extensions || {};

  var files = [];
  var loadedData = {};
  var intents = [];
  var entities = [];

  var nlp = new Bravey.Nlp[extensions.nlp || "Fuzzy"]("apiai", {
    stemmer: Bravey.Language[extensions.language].Stemmer,
    filter: extensions.filter
  });
  nlp.addEntity(new Bravey.Language[extensions.language].NumberEntityRecognizer("sys_number"));
  nlp.addEntity(new Bravey.Language[extensions.language].TimeEntityRecognizer("sys_time"));
  nlp.addEntity(new Bravey.Language[extensions.language].DateEntityRecognizer("sys_date"));
  nlp.addEntity(new Bravey.Language[extensions.language].TimePeriodEntityRecognizer("sys_time_period"));

  var pos = 0;
  var onready;

  function sanitizeApiAiId(id) {
    return id.replace(/[^a-z0-9:]/g, "_");
  }

  function loadNext() {
    Bravey.File.load(files[pos], function(text) {
      loadedData[files[pos]] = text;
      pos++;
      if (!files[pos]) dataLoaded();
      else loadNext();
    })
  }

  function dataLoaded() {
    var entity, missingEntity = {};

    for (var e = 0; e < entities.length; e++) {
      var data = JSON.parse(loadedData[entities[e].file]);
      var newEntity = new Bravey.StringEntityRecognizer(entities[e].name);
      for (var i = 0; i < data.entries.length; i++)
        for (var j = 0; j < data.entries[i].synonyms.length; j++)
          newEntity.addMatch(data.entries[i].value, data.entries[i].synonyms[j]);
      nlp.addEntity(newEntity);
    }

    for (var e = 0; e < intents.length; e++) {
      var data = JSON.parse(loadedData[intents[e].file]);
      for (var i = 0; i < data.userSays.length; i++) {
        var text = "",
          skip = false;
        for (var j = 0; j < data.userSays[i].data.length; j++) {
          if (data.userSays[i].data[j].meta) {
            entity = data.userSays[i].data[j].meta.substr(1);
            text += "@" + entity + ":" + data.userSays[i].data[j].alias;
          } else text += data.userSays[i].data[j].text;
        }
        var names = [];
        text = text.replace(/\@([.a-z0-9_-]+):([.a-z0-9_-]+)/g, function(a, b, c) {
          b = sanitizeApiAiId(b);
          c = sanitizeApiAiId(c);
          if (!nlp.hasEntity(b)) {
            skip = true;
            if (!missingEntity[b]) {
              console.warn("Missing entity", b, data.userSays[i].data);
              missingEntity[b] = 1;
            }
          }
          names.push(c);
          return "{" + b + "}";
        });
        if (!skip)
          nlp.addDocument(text.trim(), intents[e].name, {
            fromTaggedSentence: true,
            expandIntent: true,
            withNames: names
          });
      }
    }

    onready();
  }

  /**
   * Prepare for loading an intent from the specified package.
   * @param {string} name - The intent name.
   */
  this.loadIntent = function(name) {
    var filename = packagePath + "/intents/" + name + ".json";
    files.push(filename);
    intents.push({
      file: filename,
      name: name
    });
  }

  /**
   * Prepare for loading an entity from the specified package.
   * @param {string} name - The intent name.
   */
  this.loadEntity = function(name) {
    var filename = packagePath + "/entities/" + name + ".json";
    files.push(filename);
    entities.push({
      file: filename,
      name: name
    });
  }

  /**
   * Load the needed files and prepares the NLP.
   * @param {function} cb - The callback called when everything is ready.
   */
  this.prepare = function(cb) {
    onready = cb;
    loadNext();
  }

  /**
   * Check if a given sentence matches an intent and extracts its entities. Output simulates Api.ai structure. For arguments, check {@link Bravey.Nlp.test}.	
   */
  this.test = function(text, method) {
    var out = this.nlp.test(text, method);
    if (out) {
      var ret = {
        result: {
          source: "agent",
          resolvedQuery: text,
          action: "",
          actionIncomplete: false,
          parameters: {

          },
          contexts: [],
          metadata: {
            intentName: ""
          },
          fulfillment: {
            speech: ""
          },
          score: 0
        },
        status: {
          code: 200,
          errorType: "success"
        }
      };
      ret.result.metadata.intentName = out.intent;
      ret.result.score = out.score;
      for (var a in out.entitiesIndex) ret.result.parameters[a] = out.entitiesIndex[a].value;
      return ret;
    } else
      return {
        result: {
          resolvedQuery: text,
          contexts: [],
          metadata: {},
          fulfillment: {
            speech: ""
          },
          score: 0
        },
        status: {
          code: 200,
          errorType: "success"
        }
      };
  }

  /** @property {Bravey.Nlp} nlp The raw Nlp instance. */
  this.nlp = nlp;
}