UNPKG

relu-core

Version:
622 lines (621 loc) 23 kB
"use strict"; var __extends = (this && this.__extends) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; var Dialog_1 = require("./dialogs/Dialog"); var Message_1 = require("./Message"); var consts = require("./consts"); var logger = require("./logger"); var sprintf = require("sprintf-js"); var events = require("events"); var Session = (function (_super) { __extends(Session, _super); function Session(options) { var _this = _super.call(this) || this; _this.options = options; _this.msgSent = false; _this._isReset = false; _this.lastSendTime = new Date().getTime(); _this.batch = []; _this.batchStarted = false; _this.sendingBatch = false; _this.inMiddleware = false; _this._locale = null; _this.localizer = null; _this.library = options.library; _this.localizer = options.localizer; if (typeof _this.options.autoBatchDelay !== 'number') { _this.options.autoBatchDelay = 250; } return _this; } Session.prototype.toRecognizeContext = function () { var _this = this; return { message: this.message, userData: this.userData, conversationData: this.conversationData, privateConversationData: this.privateConversationData, localizer: this.localizer, dialogStack: function () { return _this.dialogStack(); }, preferredLocale: function () { return _this.preferredLocale(); }, gettext: function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return Session.prototype.gettext.call(_this, args); }, ngettext: function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return Session.prototype.ngettext.call(_this, args); }, locale: this.preferredLocale() }; }; Session.prototype.dispatch = function (sessionState, message, done) { var _this = this; var index = 0; var session = this; var now = new Date().getTime(); var middleware = this.options.middleware || []; var next = function () { var handler = index < middleware.length ? middleware[index] : null; if (handler) { index++; handler(session, next); } else { _this.inMiddleware = false; _this.sessionState.lastAccess = now; done(); } }; this.sessionState = sessionState || { callstack: [], lastAccess: now, version: 0.0 }; var cur = this.curDialog(); if (cur) { this.dialogData = cur.state; } this.inMiddleware = true; this.message = (message || { text: '' }); if (!this.message.type) { this.message.type = consts.messageType; } var locale = this.preferredLocale(); this.localizer.load(locale, function (err) { if (err) { _this.error(err); } else { next(); } }); return this; }; Session.prototype.error = function (err) { logger.info(this, 'session.error()'); if (this.options.dialogErrorMessage) { this.endConversation(this.options.dialogErrorMessage); } else { var locale = this.preferredLocale(); this.endConversation(this.localizer.gettext(locale, 'default_error', consts.Library.system)); } var m = err.toString(); err = err instanceof Error ? err : new Error(m); this.emit('error', err); return this; }; Session.prototype.preferredLocale = function (locale, callback) { if (locale) { this._locale = locale; if (this.userData) { this.userData[consts.Data.PreferredLocale] = locale; } if (this.localizer) { this.localizer.load(locale, callback); } } else if (!this._locale) { if (this.userData && this.userData[consts.Data.PreferredLocale]) { this._locale = this.userData[consts.Data.PreferredLocale]; } else if (this.message && this.message.textLocale) { this._locale = this.message.textLocale; } else if (this.localizer) { this._locale = this.localizer.defaultLocale(); } } return this._locale; }; Session.prototype.gettext = function (messageid) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } return this.vgettext(this.curLibraryName(), messageid, args); }; Session.prototype.ngettext = function (messageid, messageid_plural, count) { var tmpl; if (this.localizer && this.message) { tmpl = this.localizer.ngettext(this.preferredLocale(), messageid, messageid_plural, count, this.curLibraryName()); } else if (count == 1) { tmpl = messageid; } else { tmpl = messageid_plural; } return sprintf.sprintf(tmpl, count); }; Session.prototype.save = function () { logger.info(this, 'session.save()'); this.startBatch(); return this; }; Session.prototype.send = function (message) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } args.unshift(this.curLibraryName(), message); return Session.prototype.sendLocalized.apply(this, args); }; Session.prototype.sendLocalized = function (localizationNamespace, message) { var args = []; for (var _i = 2; _i < arguments.length; _i++) { args[_i - 2] = arguments[_i]; } this.msgSent = true; if (message) { var m; if (typeof message == 'string' || Array.isArray(message)) { m = this.createMessage(localizationNamespace, message, args); } else if (message.toMessage) { m = message.toMessage(); } else { m = message; } this.prepareMessage(m); this.batch.push(m); logger.info(this, 'session.send()'); } this.startBatch(); return this; }; Session.prototype.sendTyping = function () { this.msgSent = true; var m = { type: 'typing' }; this.prepareMessage(m); this.batch.push(m); logger.info(this, 'session.sendTyping()'); this.sendBatch(); return this; }; Session.prototype.messageSent = function () { return this.msgSent; }; Session.prototype.beginDialog = function (id, args) { logger.info(this, 'session.beginDialog(%s)', id); var id = this.resolveDialogId(id); var dialog = this.findDialog(id); if (!dialog) { throw new Error('Dialog[' + id + '] not found.'); } this.pushDialog({ id: id, state: {} }); this.startBatch(); dialog.begin(this, args); return this; }; Session.prototype.replaceDialog = function (id, args) { logger.info(this, 'session.replaceDialog(%s)', id); var id = this.resolveDialogId(id); var dialog = this.findDialog(id); if (!dialog) { throw new Error('Dialog[' + id + '] not found.'); } this.popDialog(); this.pushDialog({ id: id, state: {} }); this.startBatch(); dialog.begin(this, args); return this; }; Session.prototype.endConversation = function (message) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } var m; if (message) { if (typeof message == 'string' || Array.isArray(message)) { m = this.createMessage(this.curLibraryName(), message, args); } else if (message.toMessage) { m = message.toMessage(); } else { m = message; } this.msgSent = true; this.prepareMessage(m); this.batch.push(m); } this.privateConversationData = {}; logger.info(this, 'session.endConversation()'); var ss = this.sessionState; ss.callstack = []; this.sendBatch(); return this; }; Session.prototype.endDialog = function (message) { var args = []; for (var _i = 1; _i < arguments.length; _i++) { args[_i - 1] = arguments[_i]; } if (typeof message === 'object' && (message.hasOwnProperty('response') || message.hasOwnProperty('resumed') || message.hasOwnProperty('error'))) { console.warn('Returning results via Session.endDialog() is deprecated. Use Session.endDialogWithResult() instead.'); return this.endDialogWithResult(message); } var cur = this.curDialog(); if (cur) { var m; if (message) { if (typeof message == 'string' || Array.isArray(message)) { m = this.createMessage(this.curLibraryName(), message, args); } else if (message.toMessage) { m = message.toMessage(); } else { m = message; } this.msgSent = true; this.prepareMessage(m); this.batch.push(m); } logger.info(this, 'session.endDialog()'); var childId = cur.id; cur = this.popDialog(); this.startBatch(); if (cur) { var dialog = this.findDialog(cur.id); if (dialog) { dialog.dialogResumed(this, { resumed: Dialog_1.ResumeReason.completed, response: true, childId: childId }); } else { this.error(new Error("Can't resume missing parent dialog '" + cur.id + "'.")); } } } return this; }; Session.prototype.endDialogWithResult = function (result) { var cur = this.curDialog(); if (cur) { result = result || {}; if (!result.hasOwnProperty('resumed')) { result.resumed = Dialog_1.ResumeReason.completed; } result.childId = cur.id; logger.info(this, 'session.endDialogWithResult()'); cur = this.popDialog(); this.startBatch(); if (cur) { var dialog = this.findDialog(cur.id); if (dialog) { dialog.dialogResumed(this, result); } else { this.error(new Error("Can't resume missing parent dialog '" + cur.id + "'.")); } } } return this; }; Session.prototype.cancelDialog = function (dialogId, replaceWithId, replaceWithArgs) { var childId = typeof dialogId === 'number' ? this.sessionState.callstack[dialogId].id : dialogId; var cur = this.deleteDialogs(dialogId); if (replaceWithId) { logger.info(this, 'session.cancelDialog(%s)', replaceWithId); var id = this.resolveDialogId(replaceWithId); var dialog = this.findDialog(id); this.pushDialog({ id: id, state: {} }); this.startBatch(); dialog.begin(this, replaceWithArgs); } else { logger.info(this, 'session.cancelDialog()'); this.startBatch(); if (cur) { var dialog = this.findDialog(cur.id); if (dialog) { dialog.dialogResumed(this, { resumed: Dialog_1.ResumeReason.canceled, response: null, childId: childId }); } else { this.error(new Error("Can't resume missing parent dialog '" + cur.id + "'.")); } } } return this; }; Session.prototype.reset = function (dialogId, dialogArgs) { logger.info(this, 'session.reset()'); this._isReset = true; this.sessionState.callstack = []; if (!dialogId) { dialogId = this.options.dialogId; dialogArgs = this.options.dialogArgs; } this.beginDialog(dialogId, dialogArgs); return this; }; Session.prototype.isReset = function () { return this._isReset; }; Session.prototype.sendBatch = function (callback) { var _this = this; logger.info(this, 'session.sendBatch() sending %d messages', this.batch.length); if (this.sendingBatch) { return; } if (this.batchTimer) { clearTimeout(this.batchTimer); this.batchTimer = null; } this.batchTimer = null; var batch = this.batch; this.batch = []; this.batchStarted = false; this.sendingBatch = true; var cur = this.curDialog(); if (cur) { cur.state = this.dialogData; } this.options.onSave(function (err) { if (!err) { if (batch.length) { _this.options.onSend(batch, function (err) { _this.sendingBatch = false; if (_this.batchStarted) { _this.startBatch(); } if (callback) { callback(err); } }); } else { _this.sendingBatch = false; if (_this.batchStarted) { _this.startBatch(); } if (callback) { callback(err); } } } else { _this.sendingBatch = false; switch (err.code || '') { case consts.Errors.EBADMSG: case consts.Errors.EMSGSIZE: _this.userData = {}; _this.batch = []; _this.endConversation(_this.options.dialogErrorMessage || 'Oops. Something went wrong and we need to start over.'); break; } if (callback) { callback(err); } } }); }; Session.prototype.dialogStack = function (newStack) { var stack; if (newStack) { stack = this.sessionState.callstack = newStack; this.dialogData = stack.length > 0 ? stack[stack.length - 1].state : null; } else { stack = this.sessionState.callstack || []; if (stack.length > 0) { stack[stack.length - 1].state = this.dialogData || {}; } } return stack.slice(0); }; Session.prototype.clearDialogStack = function () { this.sessionState.callstack = []; this.dialogData = null; return this; }; Session.forEachDialogStackEntry = function (stack, reverse, fn) { var step = reverse ? -1 : 1; var l = stack ? stack.length : 0; for (var i = step > 0 ? 0 : l - 1; i >= 0 && i < l; i += step) { fn(stack[i], i); } }; Session.findDialogStackEntry = function (stack, dialogId, reverse) { if (reverse === void 0) { reverse = false; } var step = reverse ? -1 : 1; var l = stack ? stack.length : 0; for (var i = step > 0 ? 0 : l - 1; i >= 0 && i < l; i += step) { if (stack[i].id === dialogId) { return i; } } return -1; }; Session.activeDialogStackEntry = function (stack) { return stack && stack.length > 0 ? stack[stack.length - 1] : null; }; Session.pushDialogStackEntry = function (stack, entry) { if (!entry.state) { entry.state = {}; } stack = stack || []; stack.push(entry); return entry; }; Session.popDialogStackEntry = function (stack) { if (stack && stack.length > 0) { stack.pop(); } return Session.activeDialogStackEntry(stack); }; Session.pruneDialogStack = function (stack, start) { if (stack && stack.length > 0) { stack.splice(start); } return Session.activeDialogStackEntry(stack); }; Session.validateDialogStack = function (stack, root) { var valid = true; Session.forEachDialogStackEntry(stack, false, function (entry) { var pair = entry.id.split(':'); if (!root.findDialog(pair[0], pair[1])) { valid = false; } }); return valid; }; Session.prototype.routeToActiveDialog = function (recognizeResult) { var dialogStack = this.dialogStack(); if (Session.validateDialogStack(dialogStack, this.library)) { var active = Session.activeDialogStackEntry(dialogStack); if (active) { var dialog = this.findDialog(active.id); dialog.replyReceived(this, recognizeResult); } else { this.beginDialog(this.options.dialogId, this.options.dialogArgs); } } else { this.error(new Error('Invalid Dialog Stack.')); } }; Session.prototype.startBatch = function () { var _this = this; this.batchStarted = true; if (!this.sendingBatch) { if (this.batchTimer) { clearTimeout(this.batchTimer); } this.batchTimer = setTimeout(function () { _this.sendBatch(); }, this.options.autoBatchDelay); } }; Session.prototype.createMessage = function (localizationNamespace, text, args) { var message = new Message_1.Message(this) .text(this.vgettext(localizationNamespace, Message_1.Message.randomPrompt(text), args)); return message.toMessage(); }; Session.prototype.prepareMessage = function (msg) { if (!msg.type) { msg.type = 'message'; } if (!msg.address) { msg.address = this.message.address; } if (!msg.textLocale && this.message.textLocale) { msg.textLocale = this.message.textLocale; } }; Session.prototype.vgettext = function (localizationNamespace, messageid, args) { var tmpl; if (this.localizer && this.message) { tmpl = this.localizer.gettext(this.preferredLocale(), messageid, localizationNamespace); } else { tmpl = messageid; } return args && args.length > 0 ? sprintf.vsprintf(tmpl, args) : tmpl; }; Session.prototype.validateCallstack = function () { var ss = this.sessionState; for (var i = 0; i < ss.callstack.length; i++) { var id = ss.callstack[i].id; if (!this.findDialog(id)) { return false; } } return true; }; Session.prototype.resolveDialogId = function (id) { return id.indexOf(':') >= 0 ? id : this.curLibraryName() + ':' + id; }; Session.prototype.curLibraryName = function () { var cur = this.curDialog(); return cur && !this.inMiddleware ? cur.id.split(':')[0] : this.library.name; }; Session.prototype.findDialog = function (id) { var parts = id.split(':'); return this.library.findDialog(parts[0] || this.library.name, parts[1]); }; Session.prototype.pushDialog = function (ds) { var ss = this.sessionState; var cur = this.curDialog(); if (cur) { cur.state = this.dialogData || {}; } ss.callstack.push(ds); this.dialogData = ds.state || {}; return ds; }; Session.prototype.popDialog = function () { var ss = this.sessionState; if (ss.callstack.length > 0) { ss.callstack.pop(); } var cur = this.curDialog(); this.dialogData = cur ? cur.state : null; return cur; }; Session.prototype.deleteDialogs = function (dialogId) { var ss = this.sessionState; var index = -1; if (typeof dialogId === 'string') { for (var i = ss.callstack.length - 1; i >= 0; i--) { if (ss.callstack[i].id == dialogId) { index = i; break; } } } else { index = dialogId; } if (index < 0 && index < ss.callstack.length) { throw new Error('Unable to cancel dialog. Dialog[' + dialogId + '] not found.'); } ss.callstack.splice(index); var cur = this.curDialog(); this.dialogData = cur ? cur.state : null; return cur; }; Session.prototype.curDialog = function () { var cur; var ss = this.sessionState; if (ss.callstack.length > 0) { cur = ss.callstack[ss.callstack.length - 1]; } return cur; }; Session.prototype.getMessageReceived = function () { console.warn("Session.getMessageReceived() is deprecated. Use Session.message.sourceEvent instead."); return this.message.sourceEvent; }; return Session; }(events.EventEmitter)); exports.Session = Session;