UNPKG

wit-ai

Version:
279 lines (233 loc) 9.38 kB
'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _require = require('./config'); var DEFAULT_API_VERSION = _require.DEFAULT_API_VERSION; var DEFAULT_MAX_STEPS = _require.DEFAULT_MAX_STEPS; var DEFAULT_WIT_URL = _require.DEFAULT_WIT_URL; var fetch = require('isomorphic-fetch'); var log = require('./log'); var uuid = require('node-uuid'); var learnMore = 'Learn more at https://wit.ai/docs/quickstart'; function Wit(opts) { var _this = this; if (!(this instanceof Wit)) { return new Wit(opts); } var _config = this.config = Object.freeze(validate(opts)); var accessToken = _config.accessToken; var apiVersion = _config.apiVersion; var actions = _config.actions; var headers = _config.headers; var logger = _config.logger; var witURL = _config.witURL; this._sessions = {}; this.message = function (message, context) { var qs = 'q=' + encodeURIComponent(message); if (context) { qs += '&context=' + encodeURIComponent(JSON.stringify(context)); } var method = 'GET'; var fullURL = witURL + '/message?' + qs; var handler = makeWitResponseHandler(logger, 'message'); logger.debug(method, fullURL); return fetch(fullURL, { method: method, headers: headers }).then(function (response) { return Promise.all([response.json(), response.status]); }).then(handler); }; this.converse = function (sessionId, message, context, reset) { var qs = 'session_id=' + encodeURIComponent(sessionId); if (message) { qs += '&q=' + encodeURIComponent(message); } if (reset) { qs += '&reset=true'; } var method = 'POST'; var fullURL = witURL + '/converse?' + qs; var handler = makeWitResponseHandler(logger, 'converse'); logger.debug(method, fullURL); return fetch(fullURL, { method: method, headers: headers, body: JSON.stringify(context) }).then(function (response) { return Promise.all([response.json(), response.status]); }).then(handler); }; var continueRunActions = function continueRunActions(sessionId, currentRequest, message, prevContext, i) { return function (json) { if (i < 0) { logger.warn('Max steps reached, stopping.'); return prevContext; } if (currentRequest !== _this._sessions[sessionId]) { return prevContext; } if (!json.type) { throw new Error('Couldn\'t find type in Wit response'); } logger.debug('Context: ' + JSON.stringify(prevContext)); logger.debug('Response type: ' + json.type); // backwards-compatibility with API version 20160516 if (json.type === 'merge') { json.type = 'action'; json.action = 'merge'; } if (json.type === 'error') { throw new Error('Oops, I don\'t know what to do.'); } if (json.type === 'stop') { return prevContext; } var request = { sessionId: sessionId, context: clone(prevContext), text: message, entities: json.entities }; if (json.type === 'msg') { throwIfActionMissing(actions, 'send'); var response = { text: json.msg, quickreplies: json.quickreplies }; return actions.send(request, response).then(function (ctx) { if (ctx) { throw new Error('Cannot update context after \'send\' action'); } if (currentRequest !== _this._sessions[sessionId]) { return ctx; } return _this.converse(sessionId, null, prevContext).then(continueRunActions(sessionId, currentRequest, message, prevContext, i - 1)); }); } else if (json.type === 'action') { throwIfActionMissing(actions, json.action); return actions[json.action](request).then(function (ctx) { var nextContext = ctx || {}; if (currentRequest !== _this._sessions[sessionId]) { return nextContext; } return _this.converse(sessionId, null, nextContext).then(continueRunActions(sessionId, currentRequest, message, nextContext, i - 1)); }); } else { logger.debug('unknown response type ' + json.type); throw new Error('unknown response type ' + json.type); } }; }; this.runActions = function (sessionId, message, context, maxSteps) { var _this2 = this; if (!actions) throwMustHaveActions(); var steps = maxSteps ? maxSteps : DEFAULT_MAX_STEPS; // Figuring out whether we need to reset the last turn. // Each new call increments an index for the session. // We only care about the last call to runActions. // All the previous ones are discarded (preemptive exit). var currentRequest = (this._sessions[sessionId] || 0) + 1; this._sessions[sessionId] = currentRequest; var cleanup = function cleanup(ctx) { if (currentRequest === _this2._sessions[sessionId]) { delete _this2._sessions[sessionId]; } return ctx; }; return this.converse(sessionId, message, context, currentRequest > 1).then(continueRunActions(sessionId, currentRequest, message, context, steps)).then(cleanup); }; }; var makeWitResponseHandler = function makeWitResponseHandler(logger, endpoint) { return function (rsp) { var error = function error(err) { logger.error('[' + endpoint + '] Error: ' + err); throw err; }; if (rsp instanceof Error) { return error(rsp); } var _rsp = _slicedToArray(rsp, 2); var json = _rsp[0]; var status = _rsp[1]; if (json instanceof Error) { return error(json); } var err = json.error || status !== 200 && json.body + ' (' + status + ')'; if (err) { return error(err); } logger.debug('[' + endpoint + '] Response: ' + JSON.stringify(json)); return json; }; }; var throwMustHaveActions = function throwMustHaveActions() { throw new Error('You must provide the `actions` parameter to be able to use runActions. ' + learnMore); }; var throwIfActionMissing = function throwIfActionMissing(actions, action) { if (!actions[action]) { throw new Error('No \'' + action + '\' action found.'); } }; var validate = function validate(opts) { if (!opts.accessToken) { throw new Error('Could not find access token, learn more at https://wit.ai/docs'); } opts.witURL = opts.witURL || DEFAULT_WIT_URL; opts.apiVersion = opts.apiVersion || DEFAULT_API_VERSION; opts.headers = opts.headers || { 'Authorization': 'Bearer ' + opts.accessToken, 'Accept': 'application/vnd.wit.' + opts.apiVersion + '+json', 'Content-Type': 'application/json' }; opts.logger = opts.logger || new log.Logger(log.INFO); if (opts.actions) { opts.actions = validateActions(opts.logger, opts.actions); } return opts; }; var validateActions = function validateActions(logger, actions) { if ((typeof actions === 'undefined' ? 'undefined' : _typeof(actions)) !== 'object') { throw new Error('Actions should be an object. ' + learnMore); } if (!actions.send) { throw new Error('The \'send\' action is missing. ' + learnMore); } Object.keys(actions).forEach(function (key) { if (typeof actions[key] !== 'function') { logger.warn('The \'' + key + '\' action should be a function.'); } if (key === 'say' && actions[key].length > 2 || key === 'merge' && actions[key].length > 2 || key === 'error' && actions[key].length > 2) { logger.warn('The \'' + key + '\' action has been deprecated. ' + learnMore); } if (key === 'send') { if (actions[key].length !== 2) { logger.warn('The \'send\' action should accept 2 arguments: request and response. ' + learnMore); } } else if (actions[key].length !== 1) { logger.warn('The \'' + key + '\' action should accept 1 argument: request. ' + learnMore); } }); return actions; }; var clone = function clone(obj) { if (obj !== null && (typeof obj === 'undefined' ? 'undefined' : _typeof(obj)) === 'object') { if (Array.isArray(obj)) { return obj.map(clone); } else { var _ret = function () { var newObj = {}; Object.keys(obj).forEach(function (k) { newObj[k] = clone(obj[k]); }); return { v: newObj }; }(); if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v; } } else { return obj; } }; module.exports = Wit;