Source: org/terraswarm/accessor/accessors/web/services/NaturalLanguage.js

// Copyright (c) 2017 The Regents of the University of California.
// All rights reserved.
//
// Permission is hereby granted, without written agreement and without
// license or royalty fees, to use, copy, modify, and distribute this
// software and its documentation for any purpose, provided that the above
// copyright notice and the following two paragraphs appear in all copies
// of this software.
//
// IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
// FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
// ENHANCEMENTS, OR MODIFICATIONS.
//

/** Respond to textual inputs using the natural-language server from
 *  Google called API.AI (see http://api.ai). To use this, you need to
 *  create an agent at API.AI, using for example the tutorial here:
 *
 *    https://developers.google.com/actions/develop/apiai/tutorials/getting-started
 *
 *  An agent parses input text (natural language) and matches queries against
 *  rules defined in the agent to issue responses. For example, the above
 *  tutorial walks you through creating an agent that asks for your favorite
 *  number and color and then constructs a silly name by concatenating the two.
 *
 *  Once you have created an agent, you should set the '''clientAccessToken'''
 *  parameter to the hex key that identifies the agent. Without this token
 *  this accessor will not do anything.
 *
 *  This accessor is just a starting point. It should be extended
 *  to support multiple languages, for example. Also, it would be nice
 *  if there were a public agent that we could provide a default client
 *  access token for so that the accessor would work out of the box, without
 *  having to go create your own agent.
 *
 *  This accessor uses the apiai modeul, which is supported by at least
 *  the Nashorn (and CapeCode) hosts and the Node.js host.
 *  To use this under the Node.js host, install the apiai NPM module
 *  as follows:
 *  <pre>
 *    npm apiai
 *  </pre>
 *  For the Nashorn host, CapeCode includes a port of the apiai module
 *  that works under Nashorn.
 *
 *  @accessor services/NaturalLanguage
 *  @input {string} textQuery A query that the service is to respond to.
 *  @output {object} response The full response from the service.
 *  @output {string} fulfillment The fulfillment text within the response.
 *  @parameter {string} clientAccessToken The client access token for the service.
 *
 *  @author Edward A. Lee
 *  @version $$Id$$
 */

// Stop extra messages from jslint and jshint.  Note that there should
// be no space between the / and the * and global. See
// https://chess.eecs.berkeley.edu/ptexternal/wiki/Main/JSHint */
/*globals error, exports, extractFulfillment, require */
/*jshint globalstrict: true*/
'use strict';

exports.setup = function () {
    this.input('textQuery', {
        type: 'string'
    });
    this.output('response');
    this.output('fulfillment', {
        type: 'string',
        spontaneous: true
    });
    // Note that it is OK for the clientAccessToken to
    // be included in the accessor because the author needs
    // to configure an agent for this to work.
    this.parameter('clientAccessToken', {
        type: 'string',
        value: '<-- your client access token here -->'
    });
};

var apiai = require('apiai');
var util = require('util');

exports.initialize = function () {
    var self = this;
    var token = this.get('clientAccessToken');
    if (token === '<-- your client access token here -->') {
        error('You need to set clientAccessToken to a hex string that identifies\n' +
              'an agent at https://api.ai. For a tutorial on creating an agent, see\n' +
              'https://developers.google.com/actions/develop/apiai/tutorials/getting-started');
        return;
    }
    var app = apiai(this.get('clientAccessToken'));

    self.addInputHandler('textQuery', function () {
        // The session ID, I guess, disambiguates multiple users of the same
        // agent. Here, I'm just using a fixed session ID, which is risky.
        var request = app.textRequest(self.get('textQuery'), {
            sessionId: 'textQuery'
        });
        request.on('response', function (response) {
            self.send('response', response);
            self.send('fulfillment', extractFulfillment(response));
        });
        request.on('error', function (message) {
            error(message);
        });
        request.end();
    });
};

/** Given a response structure, find the fulfillment speech within it
 *  and return that. If there is no fulfillment speech in the response,
 *  then just return the entire response formatted using util.inspect().
 */
function extractFulfillment(response) {
    if (response.result &&
        response.result.fulfillment &&
        response.result.fulfillment.speech) {
        return response.result.fulfillment.speech;
    }
    return util.inspect(response);
}