UNPKG

botium-core

Version:
1,628 lines (1,443 loc) 238 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var util = _interopDefault(require('util')); var fs = _interopDefault(require('fs')); var path = _interopDefault(require('path')); var async = _interopDefault(require('async')); var mkdirp = _interopDefault(require('mkdirp')); var slugify = _interopDefault(require('slugify')); var moment = _interopDefault(require('moment')); var randomatic = _interopDefault(require('randomatic')); var lodash = _interopDefault(require('lodash')); var boolean = _interopDefault(require('boolean')); var events = _interopDefault(require('events')); var debug$h = _interopDefault(require('debug')); var vm = _interopDefault(require('vm')); var isClass = _interopDefault(require('is-class')); var glob = _interopDefault(require('glob')); var promise = _interopDefault(require('promise.allsettled')); var v1 = _interopDefault(require('uuid/v1')); var xlsx = _interopDefault(require('xlsx')); var isJson$1 = _interopDefault(require('is-json')); var sync = _interopDefault(require('csv-parse/lib/sync')); var child_process = _interopDefault(require('child_process')); var socket = _interopDefault(require('socket.io-client')); var rimraf = _interopDefault(require('rimraf')); var writeYaml = _interopDefault(require('write-yaml')); var mustache = _interopDefault(require('mustache')); var findRoot = _interopDefault(require('find-root')); var request = _interopDefault(require('request')); var copyDir = _interopDefault(require('copy-dir')); var tcpPortUsed = _interopDefault(require('tcp-port-used')); var dgram = _interopDefault(require('dgram')); var readline = _interopDefault(require('readline')); var facebookChatApi = _interopDefault(require('facebook-chat-api')); var opn = _interopDefault(require('opn')); var tinyServer = _interopDefault(require('tiny-server')); var socket$1 = _interopDefault(require('socket.io')); var promiseRetry = _interopDefault(require('promise-retry')); var jsonpath = _interopDefault(require('jsonpath')); var mimeTypes = _interopDefault(require('mime-types')); var v4 = _interopDefault(require('uuid/v4')); var esprima = _interopDefault(require('esprima')); var name = "botium-core"; var version = "1.5.4"; var description = "The Selenium for Chatbots"; var main = "index.js"; var module$1 = "dist/botium-es.js"; var engines = { node: ">=8.10.0" }; var scripts = { build: "npm run eslint && nyc npm test && nyc check-coverage --lines 50 --functions 50 --branches 35 && rollup -c", eslint: "eslint \"./src/**/*.js\" \"./test/**/*.js\"", newpatch: "npm version patch", agent: "node ./src/grid/agent/agent.js", "agent-jsdoc": "swagger-jsdoc -d ./src/grid/agent/swaggerDef.json -o ./src/grid/agent/swagger.json ./src/grid/agent/routes.js", link: "npm link botium-connector-dialogflow botium-connector-webdriverio botium-connector-directline3 botium-connector-watson botium-connector-alexa-smapi botium-connector-echo", test: "mocha \"./test/**/*.spec.js\"", "coverage:report": "nyc report --reporter=lcov npm test", "license-checker": "license-checker > LICENSES-3RDPARTY.txt" }; var repository = { type: "git", url: "git+https://github.com/codeforequity-at/botium-core.git" }; var author = "Florian Treml"; var license = "MIT"; var bugs = { url: "https://github.com/codeforequity-at/botium-core/issues" }; var homepage = "https://github.com/codeforequity-at/botium-core#readme"; var dependencies = { "@babel/runtime": "7.5.5", async: "^3.1.0", "body-parser": "^1.19.0", boolean: "^1.0.0", "copy-dir": "^1.1.0", "csv-parse": "^4.4.3", debug: "^4.1.1", esprima: "^4.0.1", express: "^4.17.1", "facebook-chat-api": "^1.6.1", "find-root": "^1.1.0", glob: "^7.1.4", "is-class": "0.0.7", "is-json": "^2.0.1", jsonpath: "^1.0.2", lodash: "^4.17.15", "mime-types": "^2.1.24", mkdirp: "^0.5.1", moment: "^2.24.0", mustache: "^3.0.1", opn: "^6.0.0", "promise-retry": "^1.1.1", "promise.allsettled": "^1.0.1", randomatic: "^3.1.1", request: "^2.88.0", rimraf: "^2.6.3", slugify: "^1.3.4", "socket.io": "^2.2.0", "socket.io-client": "^2.2.0", "socketio-auth": "^0.1.1", "swagger-jsdoc": "^3.3.0", "swagger-ui-express": "^4.0.7", "tcp-port-used": "^1.0.1", "tiny-server": "^1.1.0", uuid: "^3.3.3", "write-yaml": "^1.0.0", xlsx: "^0.14.4", xregexp: "^4.2.4" }; var devDependencies = { "@babel/core": "^7.5.5", "@babel/node": "^7.5.5", "@babel/plugin-transform-runtime": "^7.5.5", "@babel/preset-env": "^7.5.5", chai: "^4.2.0", "chai-as-promised": "^7.1.1", "cross-env": "^5.2.1", eslint: "^6.3.0", "eslint-config-standard": "^14.1.0", "eslint-plugin-import": "^2.18.2", "eslint-plugin-node": "^10.0.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "license-checker": "^25.0.1", mocha: "^6.2.0", nock: "^11.3.3", nyc: "^14.1.1", rollup: "^1.20.3", "rollup-plugin-babel": "^4.3.3", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-json": "^4.0.0", "rollup-plugin-node-resolve": "^5.2.0" }; var _package = { name: name, version: version, description: description, main: main, module: module$1, engines: engines, scripts: scripts, repository: repository, author: author, license: license, bugs: bugs, homepage: homepage, dependencies: dependencies, devDependencies: devDependencies }; var _package$1 = /*#__PURE__*/Object.freeze({ name: name, version: version, description: description, main: main, module: module$1, engines: engines, scripts: scripts, repository: repository, author: author, license: license, bugs: bugs, homepage: homepage, dependencies: dependencies, devDependencies: devDependencies, 'default': _package }); var Capabilities = { PROJECTNAME: 'PROJECTNAME', TEMPDIR: 'TEMPDIR', CLEANUPTEMPDIR: 'CLEANUPTEMPDIR', WAITFORBOTTIMEOUT: 'WAITFORBOTTIMEOUT', // "docker" "fbdirect" "watsonconversation", "simplerest", "webspeech", "inprocess" CONTAINERMODE: 'CONTAINERMODE', STARTCMD: 'STARTCMD', // falsy or ms/keystroke SIMULATE_WRITING_SPEED: 'SIMULATE_WRITING_SPEED', DOCKERCOMPOSEPATH: 'DOCKERCOMPOSEPATH', DOCKERMACHINEPATH: 'DOCKERMACHINEPATH', DOCKERMACHINE: 'DOCKERMACHINE', DOCKERIMAGE: 'DOCKERIMAGE', DOCKERUNIQUECONTAINERNAMES: 'DOCKERUNIQUECONTAINERNAMES', DOCKERSYSLOGPORT: 'DOCKERSYSLOGPORT', DOCKERSYSLOGPORT_RANGE: 'DOCKERSYSLOGPORT_RANGE', BOTIUMGRIDURL: 'BOTIUMGRIDURL', BOTIUMAPITOKEN: 'BOTIUMAPITOKEN', BOTIUMGRIDSLOT: 'BOTIUMGRIDSLOT', // General Mocker Settings BOT_HEALTH_STATUS: 'BOT_HEALTH_STATUS', BOT_HEALTH_CHECK_PATH: 'BOT_HEALTH_CHECK_PATH', BOT_HEALTH_CHECK_VERB: 'BOT_HEALTH_CHECK_VERB', // Facebook Mocker Settings FACEBOOK_API: 'FACEBOOK_API', FACEBOOK_WEBHOOK_PORT: 'FACEBOOK_WEBHOOK_PORT', FACEBOOK_WEBHOOK_PATH: 'FACEBOOK_WEBHOOK_PATH', FACEBOOK_PUBLISHPORT: 'FACEBOOK_PUBLISHPORT', FACEBOOK_PUBLISHPORT_RANGE: 'FACEBOOK_PUBLISHPORT_RANGE', FACEBOOK_SEND_DELIVERY_CONFIRMATION: 'FACEBOOK_SEND_DELIVERY_CONFIRMATION', // Slack Mocker Settings SLACK_API: 'SLACK_API', SLACK_EVENT_PORT: 'SLACK_EVENT_PORT', SLACK_EVENT_PATH: 'SLACK_EVENT_PATH', SLACK_OAUTH_PORT: 'SLACK_OAUTH_PORT', SLACK_OAUTH_PATH: 'SLACK_OAUTH_PATH', SLACK_PUBLISHPORT: 'SLACK_PUBLISHPORT', SLACK_PUBLISHPORT_RANGE: 'SLACK_PUBLISHPORT_RANGE', // Facebook Direct API Settings FB_PAGEID: 'FB_PAGEID', FB_USER: 'FB_USER', FB_PASSWORD: 'FB_PASSWORD', // Bot Framework Mocker Settings BOTFRAMEWORK_API: 'BOTFRAMEWORK_API', BOTFRAMEWORK_APP_ID: 'BOTFRAMEWORK_APP_ID', BOTFRAMEWORK_CHANNEL_ID: 'BOTFRAMEWORK_CHANNEL_ID', BOTFRAMEWORK_WEBHOOK_PORT: 'BOTFRAMEWORK_WEBHOOK_PORT', BOTFRAMEWORK_WEBHOOK_PATH: 'BOTFRAMEWORK_WEBHOOK_PATH', BOTFRAMEWORK_PUBLISHPORT: 'BOTFRAMEWORK_PUBLISHPORT', BOTFRAMEWORK_PUBLISHPORT_RANGE: 'BOTFRAMEWORK_PUBLISHPORT_RANGE', // Simple Reset Bot Settings SIMPLEREST_PING_URL: 'SIMPLEREST_PING_URL', SIMPLEREST_PING_VERB: 'SIMPLEREST_PING_VERB', SIMPLEREST_PING_BODY: 'SIMPLEREST_PING_BODY', SIMPLEREST_PING_RETRIES: 'SIMPLEREST_PING_RETRIES', SIMPLEREST_PING_TIMEOUT: 'SIMPLEREST_PING_TIMEOUT', SIMPLEREST_INIT_CONTEXT: 'SIMPLEREST_INIT_CONTEXT', SIMPLEREST_INIT_TEXT: 'SIMPLEREST_INIT_TEXT', SIMPLEREST_URL: 'SIMPLEREST_URL', SIMPLEREST_METHOD: 'SIMPLEREST_METHOD', SIMPLEREST_HEADERS_TEMPLATE: 'SIMPLEREST_HEADERS_TEMPLATE', SIMPLEREST_BODY_TEMPLATE: 'SIMPLEREST_BODY_TEMPLATE', SIMPLEREST_BODY_RAW: 'SIMPLEREST_BODY_RAW', SIMPLEREST_REQUEST_HOOK: 'SIMPLEREST_REQUEST_HOOK', SIMPLEREST_RESPONSE_JSONPATH: 'SIMPLEREST_RESPONSE_JSONPATH', SIMPLEREST_RESPONSE_HOOK: 'SIMPLEREST_RESPONSE_HOOK', SIMPLEREST_MEDIA_JSONPATH: 'SIMPLEREST_MEDIA_JSONPATH', SIMPLEREST_BUTTONS_JSONPATH: 'SIMPLEREST_BUTTONS_JSONPATH', SIMPLEREST_CONTEXT_JSONPATH: 'SIMPLEREST_CONTEXT_JSONPATH', SIMPLEREST_CONVERSATION_ID_TEMPLATE: 'SIMPLEREST_CONVERSATION_ID_TEMPLATE', SIMPLEREST_STEP_ID_TEMPLATE: 'SIMPLEREST_STEP_ID_TEMPLATE', // Webspeech Settings WEBSPEECH_SERVER_PORT: 'WEBSPEECH_SERVER_PORT', WEBSPEECH_LANGUAGE: 'WEBSPEECH_LANGUAGE', WEBSPEECH_PITCH: 'WEBSPEECH_PITCH', WEBSPEECH_RATE: 'WEBSPEECH_RATE', WEBSPEECH_VOLUME: 'WEBSPEECH_VOLUME', WEBSPEECH_VOICE: 'WEBSPEECH_VOICE', WEBSPEECH_CLOSEBROWSER: 'WEBSPEECH_CLOSEBROWSER', WEBSPEECH_BROWSER_APP: 'WEBSPEECH_BROWSER_APP', // Script Compiler SCRIPTING_TXT_EOL: 'SCRIPTING_TXT_EOL', // ROW_PER_MESSAGE or QUESTION_ANSWER SCRIPTING_XLSX_MODE: 'SCRIPTING_XLSX_MODE', SCRIPTING_XLSX_EOL_WRITE: 'SCRIPTING_XLSX_EOL_WRITE', SCRIPTING_XLSX_STARTROW: 'SCRIPTING_XLSX_STARTROW', SCRIPTING_XLSX_STARTCOL: 'SCRIPTING_XLSX_STARTCOL', SCRIPTING_XLSX_SHEETNAMES: 'SCRIPTING_XLSX_SHEETNAMES', SCRIPTING_XLSX_SHEETNAMES_PCONVOS: 'SCRIPTING_XLSX_SHEETNAMES_PCONVOS', SCRIPTING_XLSX_SHEETNAMES_UTTERANCES: 'SCRIPTING_XLSX_SHEETNAMES_UTTERANCES', SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY: 'SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY', SCRIPTING_CSV_SEPARATOR: 'SCRIPTING_CSV_SEPARATOR', SCRIPTING_CSV_USE_HEADER: 'SCRIPTING_CSV_USE_HEADER', // ROW_PER_MESSAGE or QUESTION_ANSWER SCRIPTING_CSV_MODE: 'SCRIPTING_CSV_MODE', SCRIPTING_CSV_MODE_ROW_PER_MESSAGE_COL_CONVERSATION_ID: 'SCRIPTING_CSV_MODE_ROW_PER_MESSAGE_COL_CONVERSATION_ID', SCRIPTING_CSV_MODE_ROW_PER_MESSAGE_COL_SENDER: 'SCRIPTING_CSV_MODE_ROW_PER_MESSAGE_COL_SENDER', SCRIPTING_CSV_MODE_ROW_PER_MESSAGE_COL_TEXT: 'SCRIPTING_CSV_MODE_ROW_PER_MESSAGE_COL_TEXT', SCRIPTING_CSV_MODE_QUESTION_ANSWER_COL_QUESTION: 'SCRIPTING_CSV_MODE_QUESTION_ANSWER_COL_QUESTION', SCRIPTING_CSV_MODE_QUESTION_ANSWER_COL_ANSWER: 'SCRIPTING_CSV_MODE_QUESTION_ANSWER_COL_ANSWER', SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT', SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY', SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS', // regexp, include, includeLowerCase, equals SCRIPTING_MATCHING_MODE: 'SCRIPTING_MATCHING_MODE', // all, first, random SCRIPTING_UTTEXPANSION_MODE: 'SCRIPTING_UTTEXPANSION_MODE', SCRIPTING_UTTEXPANSION_RANDOM_COUNT: 'SCRIPTING_UTTEXPANSION_RANDOM_COUNT', SCRIPTING_UTTEXPANSION_INCOMPREHENSION: 'SCRIPTING_UTTEXPANSION_INCOMPREHENSION', // Del original convo or not SCRIPTING_MEMORYEXPANSION_KEEP_ORIG: 'SCRIPTING_MEMORYEXPANSION_KEEP_ORIG', // word, non_whitespace, joker SCRIPTING_MEMORY_MATCHING_MODE: 'SCRIPTING_MEMORY_MATCHING_MODE', // Botium Lifecycle Hooks CUSTOMHOOK_ONBUILD: 'CUSTOMHOOK_ONBUILD', CUSTOMHOOK_ONSTART: 'CUSTOMHOOK_ONSTART', CUSTOMHOOK_ONSTOP: 'CUSTOMHOOK_ONSTOP', CUSTOMHOOK_ONCLEAN: 'CUSTOMHOOK_ONCLEAN', // Retry logic RETRY_USERSAYS_ONERROR_REGEXP: 'RETRY_USERSAYS_ONERROR_REGEXP', RETRY_USERSAYS_NUMRETRIES: 'RETRY_USERSAYS_NUMRETRIES', RETRY_USERSAYS_FACTOR: 'RETRY_USERSAYS_FACTOR', RETRY_USERSAYS_MINTIMEOUT: 'RETRY_USERSAYS_MINTIMEOUT', // Extension components ASSERTERS: 'ASSERTERS', LOGIC_HOOKS: 'LOGIC_HOOKS', USER_INPUTS: 'USER_INPUTS' }; var Source = { LOCALPATH: 'LOCALPATH', GITPATH: 'GITPATH', GITURL: 'GITURL', GITBRANCH: 'GITBRANCH', GITDIR: 'GITDIR', GITPREPARECMD: 'GITPREPARECMD' }; var Defaults = { Capabilities: { [Capabilities.PROJECTNAME]: 'defaultproject', [Capabilities.TEMPDIR]: 'botiumwork', [Capabilities.CLEANUPTEMPDIR]: true, [Capabilities.WAITFORBOTTIMEOUT]: 10000, [Capabilities.SIMULATE_WRITING_SPEED]: false, [Capabilities.DOCKERCOMPOSEPATH]: 'docker-compose', [Capabilities.DOCKERMACHINEPATH]: 'docker-machine', [Capabilities.DOCKERMACHINE]: false, [Capabilities.DOCKERIMAGE]: 'node:boron', [Capabilities.DOCKERUNIQUECONTAINERNAMES]: false, [Capabilities.DOCKERSYSLOGPORT_RANGE]: '47100-47299', [Capabilities.BOT_HEALTH_STATUS]: 200, [Capabilities.SLACK_PUBLISHPORT_RANGE]: '46100-46299', [Capabilities.FACEBOOK_PUBLISHPORT_RANGE]: '46300-46499', [Capabilities.FACEBOOK_SEND_DELIVERY_CONFIRMATION]: true, [Capabilities.BOTFRAMEWORK_PUBLISHPORT_RANGE]: '46500-46699', [Capabilities.BOTFRAMEWORK_WEBHOOK_PORT]: 3978, [Capabilities.BOTFRAMEWORK_WEBHOOK_PATH]: 'api/messages', [Capabilities.BOTFRAMEWORK_CHANNEL_ID]: 'facebook', [Capabilities.SIMPLEREST_PING_RETRIES]: 6, [Capabilities.SIMPLEREST_PING_TIMEOUT]: 10000, [Capabilities.SIMPLEREST_PING_VERB]: 'GET', [Capabilities.SIMPLEREST_METHOD]: 'GET', [Capabilities.WEBSPEECH_SERVER_PORT]: 46050, [Capabilities.WEBSPEECH_LANGUAGE]: 'en-US', [Capabilities.WEBSPEECH_CLOSEBROWSER]: true, [Capabilities.SCRIPTING_TXT_EOL]: '\n', [Capabilities.SCRIPTING_XLSX_EOL_WRITE]: '\r\n', [Capabilities.SCRIPTING_XLSX_STARTROW]: 2, [Capabilities.SCRIPTING_XLSX_STARTCOL]: 1, [Capabilities.SCRIPTING_NORMALIZE_TEXT]: true, [Capabilities.SCRIPTING_ENABLE_MEMORY]: false, [Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]: false, [Capabilities.SCRIPTING_MATCHING_MODE]: 'includeLowerCase', [Capabilities.SCRIPTING_UTTEXPANSION_MODE]: 'all', [Capabilities.SCRIPTING_UTTEXPANSION_RANDOM_COUNT]: 1, [Capabilities.SCRIPTING_MEMORYEXPANSION_KEEP_ORIG]: false, [Capabilities.RETRY_USERSAYS_ONERROR_REGEXP]: [], [Capabilities.RETRY_USERSAYS_NUMRETRIES]: 1, [Capabilities.RETRY_USERSAYS_FACTOR]: 1, [Capabilities.RETRY_USERSAYS_MINTIMEOUT]: 1000, [Capabilities.ASSERTERS]: [], [Capabilities.LOGIC_HOOKS]: [], [Capabilities.USER_INPUTS]: [] }, Sources: { [Source.LOCALPATH]: '.', [Source.GITPATH]: 'git', [Source.GITBRANCH]: 'master', [Source.GITDIR]: '.' }, Envs: { IS_BOTIUM_CONTAINER: true } }; var Fluent_1 = class Fluent { constructor(driver) { this.driver = driver; this.compiler = null; this.container = null; this.currentChannel = null; this.tasks = []; this.tasks.push(() => { return new Promise((resolve, reject) => { this.driver.Build().then(container => { this.container = container; resolve(); }).catch(err => { reject(err); }); }); }); } Exec() { return new Promise((resolve, reject) => { async.eachSeries(this.tasks, (task, cb) => { try { const taskResult = task(); if (taskResult && taskResult.then) { taskResult.then(() => cb()).catch(cb); } else { cb(); } } catch (err) { cb(err); } }, err => { if (err) return reject(err); resolve(); }); }); } Start() { this.tasks.push(() => { return this.container.Start(); }); return this; } SwitchChannel(channel) { this.tasks.push(() => { this.currentChannel = channel; return Promise.resolve(); }); return this; } ReadScripts(convoDir, globFilter) { this.tasks.push(() => { return new Promise((resolve, reject) => { if (this.compiler == null) { try { this.compiler = this.driver.BuildCompiler(); } catch (err) { return reject(err); } } try { this.compiler.ReadScriptsFromDirectory(convoDir, globFilter); resolve(); } catch (err) { reject(err); } }); }); return this; } Compile(scriptBuffer, scriptFormat, scriptType) { this.tasks.push(() => { return new Promise((resolve, reject) => { if (this.compiler == null) { try { this.compiler = this.driver.BuildCompiler(); } catch (err) { return reject(err); } } try { this.compiler.Compile(scriptBuffer, scriptFormat, scriptType); resolve(); } catch (err) { reject(err); } }); }); return this; } RunScripts(assertCb, failCb) { this.tasks.push(() => { return new Promise((resolve, reject) => { if (assertCb) { this.compiler.scriptingEvents.assertBotResponse = assertCb.bind(this.compiler); } if (failCb) { this.compiler.scriptingEvents.fail = failCb.bind(this.compiler); } this.compiler.ExpandConvos(); async.eachSeries(this.compiler.convos, (convo, convoDone) => { this.container.Start().then(() => convo.Run(this.container)).then(() => this.container.Stop()).then(() => convoDone()).catch(convoDone); }, err => { if (err) return reject(err);else resolve(); }); }); }); return this; } UserSaysText(msg) { this.tasks.push(() => { if (lodash.isFunction(msg)) { msg = msg(); } if (this.currentChannel) { return this.container.UserSays({ messageText: msg, channel: this.currentChannel }); } else { return this.container.UserSaysText(msg); } }); return this; } UserSays(msg) { this.tasks.push(() => { if (lodash.isFunction(msg)) { msg = msg(); } if (this.currentChannel && !msg.channel) { msg = Object.assign({}, msg); msg.channel = this.currentChannel; } return this.container.UserSays(msg); }); return this; } WaitBotSays(channel = null, timeoutMillis = null, callback = null) { if (!callback) { if (timeoutMillis && lodash.isFunction(timeoutMillis)) { callback = timeoutMillis; timeoutMillis = null; } else if (!timeoutMillis && channel && lodash.isFunction(channel)) { callback = channel; timeoutMillis = null; channel = null; } } this.tasks.push(() => { return new Promise((resolve, reject) => { if (this.currentChannel && !channel) { channel = this.currentChannel; } this.container.WaitBotSays(channel, timeoutMillis).then(botMsg => { if (callback) callback(botMsg); resolve(); }).catch(err => { reject(err); }); }); }); return this; } WaitBotSaysText(channel = null, timeoutMillis = null, callback = null) { if (!callback) { if (timeoutMillis && lodash.isFunction(timeoutMillis)) { callback = timeoutMillis; timeoutMillis = null; } else if (!timeoutMillis && channel && lodash.isFunction(channel)) { callback = channel; timeoutMillis = null; channel = null; } } this.tasks.push(() => { return new Promise((resolve, reject) => { if (this.currentChannel && !channel) { channel = this.currentChannel; } this.container.WaitBotSaysText(channel, timeoutMillis).then(text => { if (callback) callback(text); resolve(); }).catch(err => { reject(err); }); }); }); return this; } Restart() { this.tasks.push(() => { return this.container.Restart(); }); return this; } Stop() { this.tasks.push(() => { return this.container.Stop(); }); return this; } Clean() { this.tasks.push(() => { return this.container.Clean(); }); return this; } Call(customFunction) { this.tasks.push(() => { return customFunction(this) || Promise.resolve(); }); return this; } }; var Events = { // Botium Events CONTAINER_BUILDING: 'CONTAINER_BUILDING', CONTAINER_BUILT: 'CONTAINER_BUILT', CONTAINER_BUILD_ERROR: 'CONTAINER_BUILD_ERROR', CONTAINER_STARTING: 'CONTAINER_STARTING', CONTAINER_STARTED: 'CONTAINER_STARTED', CONTAINER_START_ERROR: 'CONTAINER_START_ERROR', CONTAINER_STOPPING: 'CONTAINER_STOPPING', CONTAINER_STOPPED: 'CONTAINER_STOPPED', CONTAINER_STOP_ERROR: 'CONTAINER_STOP_ERROR', CONTAINER_CLEANING: 'CONTAINER_CLEANING', CONTAINER_CLEANED: 'CONTAINER_CLEANED', CONTAINER_CLEAN_ERROR: 'CONTAINER_CLEAN_ERROR', BOT_CONNECTED: 'BOT_CONNECTED', // Chatbot Events MESSAGE_SENTTOBOT: 'MESSAGE_SENTTOBOT', MESSAGE_SENDTOBOT_ERROR: 'MESSAGE_SENDTOBOT_ERROR', MESSAGE_RECEIVEDFROMBOT: 'MESSAGE_RECEIVEDFROMBOT', MESSAGE_RECEIVEFROMBOT_ERROR: 'MESSAGE_RECEIVEFROMBOT_ERROR', MESSAGE_ATTACHMENT: 'MESSAGE_ATTACHMENT', MESSAGE_TRANSCRIPT: 'MESSAGE_TRANSCRIPT', // Botium Agent Events TOOMUCHWORKERS_ERROR: 'TOOMUCHWORKERS_ERROR' }; function commonjsRequire () { throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); } function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } function getCjsExportFromNamespace (n) { return n && n['default'] || n; } var LogicHookConsts = { LOGIC_HOOK_INCLUDE: 'INCLUDE' }; const debug = debug$h('botium-asserterUtils'); const { LOGIC_HOOK_INCLUDE } = LogicHookConsts; const DEFAULT_ASSERTERS = [{ name: 'BUTTONS', className: 'ButtonsAsserter' }, { name: 'MEDIA', className: 'MediaAsserter' }, { name: 'CARDS', className: 'CardsAsserter' }, { name: 'PAUSE_ASSERTER', className: 'PauseAsserter' }, { name: 'ENTITIES', className: 'EntitiesAsserter' }, { name: 'ENTITY_VALUES', className: 'EntityValuesAsserter' }, { name: 'INTENT', className: 'IntentAsserter' }, { name: 'INTENT_UNIQUE', className: 'IntentUniqueAsserter' }, { name: 'INTENT_CONFIDENCE', className: 'IntentConfidenceAsserter' }, { name: 'JSON_PATH', className: 'JsonPathAsserter' }]; DEFAULT_ASSERTERS.forEach(asserter => { asserter.Class = commonjsRequire(`./asserter/${asserter.className}`); }); const DEFAULT_LOGIC_HOOKS = [{ name: 'PAUSE', className: 'PauseLogicHook' }, { name: 'WAITFORBOT', className: 'WaitForBotLogicHook' }, { name: 'SET_SCRIPTING_MEMORY', className: 'SetScriptingMemoryLogicHook' }, { name: 'CLEAR_SCRIPTING_MEMORY', className: 'ClearScriptingMemoryLogicHook' }, { name: 'UPDATE_CUSTOM', className: 'UpdateCustomLogicHook' }, { name: LOGIC_HOOK_INCLUDE, className: 'IncludeLogicHook' }]; DEFAULT_LOGIC_HOOKS.forEach(logicHook => { logicHook.Class = commonjsRequire(`./logichooks/${logicHook.className}`); }); const DEFAULT_USER_INPUTS = [{ name: 'BUTTON', className: 'ButtonInput' }, { name: 'MEDIA', className: 'MediaInput' }, { name: 'FORM', className: 'FormInput' }]; DEFAULT_USER_INPUTS.forEach(userInput => { userInput.Class = commonjsRequire(`./userinput/${userInput.className}`); }); var LogicHookUtils_1 = class LogicHookUtils { constructor({ buildScriptContext, caps }) { this.asserters = {}; this.globalAsserters = []; this.logicHooks = {}; this.globalLogicHooks = []; this.userInputs = {}; this.buildScriptContext = buildScriptContext; this.caps = caps; this._setDefaultAsserters(); this._setDefaultLogicHooks(); this._setDefaultUserInputs(); this._fetchAsserters(); this._fetchLogicHooks(); this._fetchUserInputs(); } _setDefaultAsserters() { DEFAULT_ASSERTERS.forEach(asserter => { this.asserters[asserter.name] = new asserter.Class(this.buildScriptContext, this.caps); }); debug(`Loaded Default asserter - ${util.inspect(Object.keys(this.asserters))}`); } _setDefaultLogicHooks() { DEFAULT_LOGIC_HOOKS.forEach(lh => { this.logicHooks[lh.name] = new lh.Class(this.buildScriptContext, this.caps); }); debug(`Loaded Default logic hook - ${util.inspect(Object.keys(this.logicHooks))}`); } _setDefaultUserInputs() { DEFAULT_USER_INPUTS.forEach(ui => { this.userInputs[ui.name] = new ui.Class(this.buildScriptContext, this.caps); }); debug(`Loaded Default user input - ${util.inspect(Object.keys(this.userInputs))}`); } _fetchAsserters() { this.caps[Capabilities.ASSERTERS].map(asserter => { if (this.asserters[asserter.ref]) { debug(`${asserter.ref} asserter already exists, overwriting.`); } this.asserters[asserter.ref] = this._loadClass(asserter, 'asserter'); if (asserter.global) { this.globalAsserters.push(asserter.ref); debug(`global asserter: ${asserter.ref} was set and will be executed in every convo`); } }); } _fetchLogicHooks() { this.caps[Capabilities.LOGIC_HOOKS].map(logicHook => { if (this.logicHooks[logicHook.ref]) { debug(`${logicHook.ref} logic hook already exists, overwriting.`); } this.logicHooks[logicHook.ref] = this._loadClass(logicHook, 'logic-hook'); debug(`Loaded ${logicHook.ref} SUCCESSFULLY`); if (logicHook.global) { this.globalLogicHooks.push(logicHook.ref); debug(`global logic hook: ${logicHook.ref} was set and will be executed in every convo`); } }); } _fetchUserInputs() { this.caps[Capabilities.USER_INPUTS].map(userInput => { if (this.userInputs[userInput.ref]) { debug(`${userInput.ref} userinput already exists, overwriting.`); } this.userInputs[userInput.ref] = this._loadClass(userInput, 'user-input'); debug(`Loaded ${userInput.ref} SUCCESSFULLY`); }); } getGlobalAsserter() { return this.globalAsserters.map(name => this.asserters[name]); } getGlobalLogicHook() { return this.globalLogicHooks.map(name => this.logicHooks[name]); } _loadClass({ src, ref, args }, hookType) { if (hookType !== 'asserter' && hookType !== 'logic-hook' && hookType !== 'user-input') { throw Error(`Unknown hookType ${hookType}`); } // 1 gives possibility to use default asserter as global asserter if (hookType === 'asserter') { const asserter = DEFAULT_ASSERTERS.find(asserter => src === asserter.className); if (asserter) { debug(`Loading ${ref} ${hookType}. Using default asserter ${asserter.className} as global asserter`); return new asserter.Class(this.buildScriptContext, this.caps, args); } } if (hookType === 'logic-hook') { const lh = DEFAULT_LOGIC_HOOKS.find(lh => src === lh.className); if (lh) { debug(`Loading ${ref} ${hookType}. Using default logic-hook ${lh.className} as global logic-hook`); return new lh.Class(this.buildScriptContext, this.caps, args); } } if (hookType === 'user-input') { const ui = DEFAULT_USER_INPUTS.find(ui => src === ui.className); if (ui) { debug(`Loading ${ref} ${hookType}. Using default user-input ${ui.className} as global user-input`); return new ui.Class(this.buildScriptContext, this.caps, args); } } if (!src) { const packageName = `botium-${hookType}-${ref}`; try { const CheckClass = commonjsRequire(packageName); if (isClass(CheckClass)) { debug(`Loading ${ref} ${hookType}. Loading from ${packageName} as class. Guessed package name.`); return new CheckClass(this.buildScriptContext, this.caps, args); } else if (lodash.isFunction(CheckClass)) { debug(`Loading ${ref} ${hookType}. Loading from ${packageName} as function. Guessed package name.`); return CheckClass(this.buildScriptContext, this.caps, args); } else { throw new Error(`${packageName} class or function expected`); } } catch (err) { throw new Error(`Failed to fetch package ${packageName} - ${util.inspect(err)}`); } } if (isClass(src)) { try { const CheckClass = src; debug(`Loading ${ref} ${hookType}. Using src as class.`); return new CheckClass(this.buildScriptContext, this.caps, args); } catch (err) { throw new Error(`Failed to load package ${ref} from provided class - ${util.inspect(err)}`); } } if (lodash.isFunction(src)) { try { debug(`Loading ${ref} ${hookType}. Using src as function.`); return src(this.buildScriptContext, this.caps, args); } catch (err) { throw new Error(`Failed to load package ${ref} from provided function - ${util.inspect(err)}`); } } if (lodash.isObject(src) && !lodash.isString(src)) { try { const hookObject = Object.keys(src).reduce((result, key) => { result[key] = args => { const script = src[key]; try { const sandbox = vm.createContext({ debug, console, ...args }); vm.runInContext(script, sandbox); return sandbox.result || Promise.resolve(); } catch (err) { throw new Error(`Script "${key}" is not valid - ${util.inspect(err)}`); } }; return result; }, {}); debug(`Loading ${ref} ${hookType}. Using src as function code.`); return hookObject; } catch (err) { throw new Error(`Failed to load package ${ref} from provided function - ${util.inspect(err)}`); } } const loadErr = []; const tryLoadPackage = src; try { let CheckClass = commonjsRequire(tryLoadPackage); if (CheckClass.default) { CheckClass = CheckClass.default; } if (isClass(CheckClass)) { debug(`Loading ${ref} ${hookType}. Using src for require. Loading from ${tryLoadPackage} as class`); return new CheckClass(this.buildScriptContext, this.caps, args); } else if (lodash.isFunction(CheckClass)) { debug(`Loading ${ref} ${hookType}. Using src for require. Loading from ${tryLoadPackage} as class`); return CheckClass(this.buildScriptContext, this.caps, args); } else { throw new Error(`${tryLoadPackage} class or function expected`); } } catch (err) { loadErr.push(`Failed to fetch ${ref} ${hookType} from ${tryLoadPackage} - ${util.inspect(err)}`); } const tryLoadFile = path.resolve(process.cwd(), src); try { let CheckClass = commonjsRequire(tryLoadFile); if (CheckClass.default) { CheckClass = CheckClass.default; } if (isClass(CheckClass)) { debug(`Loading ${ref} ${hookType}. Using src as relative path to module with a class. Loading from ${tryLoadFile} as class`); return new CheckClass(this.buildScriptContext, this.caps, args); } else if (lodash.isFunction(CheckClass)) { debug(`Loading ${ref} ${hookType}. Using src as relative path to module with a function. Loading from ${tryLoadFile} as class`); return CheckClass(this.buildScriptContext, this.caps, args); } else { throw new Error(`${tryLoadFile} class or function expected`); } } catch (err) { loadErr.push(`Failed to fetch ${ref} ${hookType} from ${tryLoadFile} - ${util.inspect(err)} `); } loadErr.forEach(debug); throw new Error(`Failed to fetch ${ref} ${hookType}, no idea how to load ...`); } }; var Constants = { SCRIPTING_FORMAT_XSLX: 'SCRIPTING_FORMAT_XSLX', SCRIPTING_FORMAT_TXT: 'SCRIPTING_FORMAT_TXT', SCRIPTING_FORMAT_YAML: 'SCRIPTING_FORMAT_YAML', SCRIPTING_FORMAT_CSV: 'SCRIPTING_FORMAT_CSV', SCRIPTING_TYPE_CONVO: 'SCRIPTING_TYPE_CONVO', SCRIPTING_TYPE_PCONVO: 'SCRIPTING_TYPE_PCONVO', SCRIPTING_TYPE_UTTERANCES: 'SCRIPTING_TYPE_UTTERANCES', SCRIPTING_TYPE_SCRIPTING_MEMORY: 'SCRIPTING_TYPE_SCRIPTING_MEMORY' }; var BotiumMockAttachment_1 = class BotiumMockAttachment { constructor(fromJson = {}) { this.name = fromJson.name; this.mimeType = fromJson.mimeType; this.base64 = fromJson.base64; } }; class BotiumMockMedia { constructor(fromJson = {}) { this.mediaUri = fromJson.mediaUri; this.mimeType = fromJson.mimeType; this.altText = fromJson.altText; } } class BotiumMockButton { constructor(fromJson = {}) { this.text = fromJson.text; this.payload = fromJson.payload; this.imageUri = fromJson.imageUri; } } class BotiumMockCard { constructor(fromJson = {}) { this.text = fromJson.text; this.subtext = fromJson.subtext; this.content = fromJson.content; this.image = fromJson.image ? new BotiumMockMedia(fromJson.image) : null; this.buttons = fromJson.buttons ? fromJson.buttons.map(a => new BotiumMockButton(a)) : null; this.media = fromJson.media ? fromJson.media.map(a => new BotiumMockMedia(a)) : null; } } class BotiumMockForm { constructor(fromJson = {}) { this.name = fromJson.name; this.value = fromJson.value; } } var BotiumMockRichMessageTypes = { BotiumMockMedia, BotiumMockButton, BotiumMockCard, BotiumMockForm }; class BotiumMockAsserter { constructor(fromJson = {}) { this.name = fromJson.name; this.args = lodash.cloneDeep(fromJson.args); } } class BotiumMockUserInput { constructor(fromJson = {}) { this.name = fromJson.name; this.args = lodash.cloneDeep(fromJson.args); } } class BotiumMockLogicHook { constructor(fromJson = {}) { this.name = fromJson.name; this.args = lodash.cloneDeep(fromJson.args); } } var BotiumMockScripting = { BotiumMockAsserter, BotiumMockUserInput, BotiumMockLogicHook }; const { BotiumMockMedia: BotiumMockMedia$1, BotiumMockButton: BotiumMockButton$1, BotiumMockCard: BotiumMockCard$1, BotiumMockForm: BotiumMockForm$1 } = BotiumMockRichMessageTypes; const { BotiumMockAsserter: BotiumMockAsserter$1, BotiumMockUserInput: BotiumMockUserInput$1, BotiumMockLogicHook: BotiumMockLogicHook$1 } = BotiumMockScripting; var BotiumMockMessage_1 = class BotiumMockMessage { constructor(fromJson = {}) { this.sender = fromJson.sender; this.channel = fromJson.channel; this.messageText = fromJson.messageText; this.media = fromJson.media ? fromJson.media.map(a => new BotiumMockMedia$1(a)) : null; this.buttons = fromJson.buttons ? fromJson.buttons.map(a => new BotiumMockButton$1(a)) : null; this.cards = fromJson.cards ? fromJson.cards.map(a => new BotiumMockCard$1(a)) : null; this.forms = fromJson.forms ? fromJson.forms.map(a => new BotiumMockForm$1(a)) : null; this.nlp = fromJson.nlp; this.sourceData = fromJson.sourceData; this.sourceAction = fromJson.sourceAction; this.attachments = fromJson.attachments ? fromJson.attachments.map(a => new BotiumMockAttachment_1(a)) : null; this.asserters = fromJson.asserters ? fromJson.asserters.map(a => new BotiumMockAsserter$1(a)) : null; this.userInputs = fromJson.userInputs ? fromJson.userInputs.map(a => new BotiumMockUserInput$1(a)) : null; this.logicHooks = fromJson.logicHooks ? fromJson.logicHooks.map(a => new BotiumMockLogicHook$1(a)) : null; } }; const debug$1 = debug$h('botium-ScriptingMemory'); // If they got parameter, then it will be a string always. // the receiver can decide what to do with it, // convert to int, // split by ',' for multiple params, // or something else const SCRIPTING_FUNCTIONS = { $now: () => { return new Date().toLocaleString(); }, $now_EN: () => { return new Date().toLocaleString('en-EN'); }, $now_DE: () => { return moment().format('DD.MM.YYYY, HH:mm:ss'); }, $now_ISO: () => { return new Date().toISOString(); }, $date: pattern => { if (pattern) { return moment().format(pattern); } return new Date().toLocaleDateString(); }, $date_EN: () => { return new Date().toLocaleDateString('en-EN'); }, $date_DE: () => { return moment().format('YYYY.MM.DD'); }, $date_ISO: () => { return moment().format('YYYY-MM-DD'); }, $time: () => { return new Date().toLocaleTimeString(); }, $time_EN: () => { return new Date().toLocaleTimeString('en-EN'); }, $time_DE: () => { return moment().format('HH:mm:ss'); }, $time_ISO: () => { return moment().format('HH:mm:ss'); }, $time_HH_MM: () => { return moment().format('HH:mm'); }, $time_HH: () => { return moment().format('HH'); }, $time_H_A: () => { return moment().format('h A'); }, $timestamp: () => { return Date.now(); }, $year: () => { return new Date().getFullYear(); }, $month: () => { return moment().format('MMMM'); }, $month_MM: () => { return moment().format('MM'); }, $day_of_month: () => { return new Date().getDate(); }, $day_of_week: () => { return moment().format('dddd'); }, $random: length => { if (length == null) { throw Error('random function used without args!'); } return randomatic('0', length); }, $random10: () => { return randomatic('0', 10); }, $uniqid: () => { return v1(); }, $func: code => { if (code == null) { throw Error('func function used without args!'); } try { return vm.runInNewContext(code, { debug: debug$1, console: console, require: commonjsRequire }); } catch (err) { throw Error(`func function execution failed - ${err}`); } } }; const RESERVED_WORDS = Object.keys(SCRIPTING_FUNCTIONS); const apply = (container, scriptingMemory, str) => { if (container.caps[Capabilities.SCRIPTING_ENABLE_MEMORY]) { str = _apply(scriptingMemory, str); } return str; }; const applyToArgs = (args, scriptingMemory) => { return (args || []).map(arg => { return _apply(scriptingMemory, arg); }); }; // we have two replace longer variable first. if there is $year, and $years, $years should not be found by $year const _longestFirst = (a, b) => b.length - a.length; const _apply = (scriptingMemory, str) => { if (str) { // merge all keys: longer is stronger, type does not matter // Not clean: what if a variable references other variable/function? const allKeys = Object.keys(SCRIPTING_FUNCTIONS).concat(Object.keys(scriptingMemory)).sort(_longestFirst); for (const key of allKeys) { // scripting memory is stronger if (scriptingMemory[key]) { str = str.replace(key, scriptingMemory[key]); } else { const regex = `\\${key}(\\(.+(?<!\\\\)\\))?`; const matches = str.match(new RegExp(regex, 'g')) || []; for (const match of matches) { if (match.indexOf('(') > 0) { const arg = match.substring(match.indexOf('(') + 1, match.lastIndexOf(')')).replace(/\\\)/g, ')'); str = str.replace(match, SCRIPTING_FUNCTIONS[key](arg)); } else { str = str.replace(match, SCRIPTING_FUNCTIONS[key]()); } } } } } return str; }; const fill = (container, scriptingMemory, result, utterance, scriptingEvents) => { debug$1(`fill start: ${util.inspect(scriptingMemory)}`); let varRegex; switch (container.caps[Capabilities.SCRIPTING_MEMORY_MATCHING_MODE]) { case 'word': varRegex = '(\\w+)'; break; case 'joker': varRegex = '([\\w\\W]+)'; break; default: varRegex = '(\\S+)'; break; } if (result && lodash.isString(result) && utterance && container.caps[Capabilities.SCRIPTING_ENABLE_MEMORY]) { const utterances = scriptingEvents.resolveUtterance({ utterance }); utterances.forEach(expected => { let reExpected = expected; if (container.caps[Capabilities.SCRIPTING_MATCHING_MODE] !== 'regexp') { reExpected = lodash.isString(expected) ? expected.replace(/[-\\^*+?.()|[\]{}]/g, '\\$&') : expected; } const varMatches = ((lodash.isString(expected) ? expected.match(/\$[A-Za-z]\w+/g) : false) || []).sort(_longestFirst); for (let i = 0; i < varMatches.length; i++) { reExpected = reExpected.replace(varMatches[i], varRegex); } const resultMatches = result.match(reExpected) || []; for (let i = 1; i < resultMatches.length; i++) { if (i <= varMatches.length) { const varName = varMatches[i - 1]; if (RESERVED_WORDS.indexOf(varName) >= 0) { debug$1(`fill Variable "${varName}" is not overwritten, because it is reserved word. `); } else { scriptingMemory[varName] = resultMatches[i]; } } } }); debug$1(`fill end: ${util.inspect(scriptingMemory)}`); } }; var ScriptingMemory = { apply, applyToArgs, fill, RESERVED_WORDS, SCRIPTING_FUNCTIONS }; const BotiumError = class BotiumError extends Error { /** * * @param message * @param context A JSON with struct * { * type: 'some free text to identity the exception type', * source: 'source of the event', * ... */ constructor(message, context, supressChildCheck) { super(message.message || message); if (!supressChildCheck && _getChildErrorsFromContext(context)) { throw Error('Create BotiumError with child errors using the fromList() method!'); } // Saving class name in the property of our custom error as a shortcut. this.name = this.constructor.name; // Capturing stack trace, excluding constructor call from it. Error.captureStackTrace(this, this.constructor); this.context = context || {}; this.context.message = message.message || message; } }; const _getChildErrorsFromContext = context => { if (context && context.errors && lodash.isArray(context.errors)) { return context.errors; } return false; }; const _getChildErrorsFromError = error => { if (error.context) { return _getChildErrorsFromContext(error.context); } return false; }; var getErrorsFromError = (error, safe = true) => { const childErrors = _getChildErrorsFromError(error); if (childErrors) { return childErrors; } if (safe) { return error; } throw Error('Invalid error format!'); }; const botiumErrorFromErr = (message, err) => { if (err instanceof BotiumError) { return new BotiumError(message, err.context, true); } else { return new BotiumError(message, { err }, true); } }; const botiumErrorFromList = (errors, { type = 'list', source = 'BotiumError', flat = true }) => { const message = errors.map(err => err.message || err.toString()).join(',\n'); let children = []; for (const error of errors) { if (error instanceof BotiumError) { const childErrors = flat && _getChildErrorsFromContext(error.context); if (childErrors && childErrors.length) { children = children.concat(childErrors); } else if (error.context) { children.push(error.context); } } else { children.push(error); } } const result = new BotiumError(message, { errors: children, type, source }, true); return result; }; var BotiumError_1 = { BotiumError, botiumErrorFromErr, botiumErrorFromList }; BotiumError_1.getErrorsFromError = getErrorsFromError; const debug$2 = debug$h('botium-Convo'); const { BotiumError: BotiumError$1, botiumErrorFromErr: botiumErrorFromErr$1, botiumErrorFromList: botiumErrorFromList$1 } = BotiumError_1; const { LOGIC_HOOK_INCLUDE: LOGIC_HOOK_INCLUDE$1 } = LogicHookConsts; class ConvoHeader { constructor(fromJson = {}) { this.name = fromJson.name; this.sort = fromJson.sort; this.order = fromJson.order; this.description = fromJson.description; } toString() { return this.order + ' ' + this.name + (this.description ? ` (${this.description})` : ''); } } class ConvoStepAssert { constructor(fromJson = {}) { this.name = fromJson.name; this.args = fromJson.args; } toString() { return this.name + '(' + (this.args ? this.args.join(',') : 'no args') + ')'; } } class ConvoStepLogicHook { constructor(fromJson = {}) { this.name = fromJson.name; this.args = fromJson.args; } toString() { return this.name + '(' + (this.args ? this.args.join(',') : 'no args') + ')'; } } class ConvoStepUserInput { constructor(fromJson = {}) { this.name = fromJson.name; this.args = fromJson.args; } toString() { return this.name + '(' + (this.args ? this.args.join(',') : 'no args') + ')'; } } class ConvoStep { constructor(fromJson = {}) { this.sender = fromJson.sender; this.channel = fromJson.channel; this.messageText = fromJson.messageText; this.sourceData = fromJson.sourceData; this.stepTag = fromJson.stepTag; this.not = fromJson.not; this.asserters = lodash.map(fromJson.asserters, asserter => new ConvoStepAssert(asserter)); this.logicHooks = lodash.map(fromJson.logicHooks, logicHook => new ConvoStepLogicHook(logicHook)); this.userInputs = lodash.map(fromJson.userInputs, userInput => new ConvoStepUserInput(userInput)); } hasInteraction() { return this.messageText && this.messageText.length > 0 || this.sourceData || this.asserters && this.asserters.length > 0 || this.logicHooks && this.logicHooks.findIndex(l => l.name !== LOGIC_HOOK_INCLUDE$1) >= 0 || this.userInputs && this.userInputs.length > 0; } toString() { return this.stepTag + ': #' + this.sender + ' - ' + (this.not ? '!' : '') + this.messageText + (this.asserters && this.asserters.length > 0 ? ' ' + this.asserters.map(a => a.toString()).join(' ASS: ') : '') + (this.logicHooks && this.logicHooks.length > 0 ? ' ' + this.logicHooks.map(l => l.toString()).join(' LH: ') : '') + (this.userInputs && this.userInputs.length > 0 ? ' ' + this.userInputs.map(u => u.toString()).join(' UI: ') : ''); } } class Transcript { constructor({ steps, scriptingMemory, convoBegin, convoEnd, err }) { this.steps = steps; this.scriptingMemory = scriptingMemory; this.convoBegin = convoBegin; this.convoEnd = convoEnd; this.err = err; } } class TranscriptStep { constructor({ expected, not, actual, stepBegin, stepEnd, botBegin, botEnd, err }) { this.expected = expected; this.not = not; this.actual = actual; this.stepBegin = stepBegin; this.stepEnd = stepEnd; this.botBegin = botBegin; this.botEnd = botEnd; this.err = err; } } class TranscriptError extends Error { constructor(err, transcript) { super(err.message); this.name = this.constructor.name; this.transcript = transcript; this.cause = err; Error.captureStackTrace(this, this.constructor); } } class Convo { constructor(context, fromJson = {}) { if (fromJson instanceof Convo) { debug$2('Illegal state!!! Parameter should be a JSON, but it is a Convo'); } else if (fromJson.beginAsserter) { // beginAsserter is one of the fields which are lost debug$2('Illegal state!!! Parameter should be a native JSON, but looks as a Convo converted to JSON'); } this.scriptingEvents = context.scriptingEvents; this.context = context; this.header = new ConvoHeader(fromJson.header); if (fromJson.conversation && lodash.isArray(fromJson.conversation)) { this.conversation = lodash.map(fromJson.conversation, step => new ConvoStep(step)); } else { this.conversation = []; } this.sourceTag = fromJson.sourceTag; const { beginAsserter, endAsserter } = this.setConvoBeginAndEndAsserter(fromJson); this.beginAsserter = beginAsserter; this.endAsserter = endAsserter; const { beginLogicHook, endLogicHook } = this.setConvoBeginAndEndLogicHook(fromJson); this.beginLogicHook = beginLogicHook; this.endLogicHook = endLogicHook; this.effectiveConversation = null; } setConvoBeginAndEndAsserter(fromJson) { const beginAsserter = fromJson.conversation.filter(s => s.sender === 'begin' && s.asserters && s.asserters.length > 0).map(s => s.asserters).reduce((acc, val) => acc.concat(val), []); const endAsserter = fromJson.conversation.filter(s => s.sender === 'end' && s.asserters && s.asserters.length > 0).map(s => s.asserters).reduce((acc, val) => acc.concat(val), []); return { beginAsserter, endAsserter }; } setConvoBeginAndEndLogicHook(fromJson) { const beginLogicHook = fromJson.conversation.filter(s => s.sender === 'begin' && s.logicHooks && s.logicHooks.length > 0).map(s => s.logicHooks).reduce((acc, val) => acc.concat(val), []); const endLogicHook = fromJson.conversation.filter(s => s.sender === 'end' && s.logicHooks && s.logicHooks.length > 0).map(s => s.logicHooks).reduce((acc, val) => acc.concat(val), []); return { beginLogicHook, endLogicHook }; } toString() { return this.header.toString() + (this.sourceTag ? ` (${util.inspect(this.sourceTag)})` : '') + ': ' + this.conversation.map(c => c.toString()).join(' | '); } Run(container) { return new Promise((resolve, reject) => { const scriptingMemory = {}; async.waterfall([// onConvoBegin first or assertConvoBegin? If onConvoBegin, then it is possible to assert it too cb => { this.scriptingEvents.onConvoBegin({ convo: this, container, scriptingMemory }).then(() => cb()).catch(err => cb(botiumErrorFromErr$1(`${this.header.name}: error begin handler - ${err.message}`, err))); }, cb => { this.scriptingEvents.assertConvoBegin({ convo: