botium-core 1.13.18 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/botium-cjs.js +326 -387
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +326 -385
  4. package/dist/botium-es.js.map +1 -1
  5. package/package.json +21 -25
  6. package/samples/extensions/asserterHooks/botium.json +1 -0
  7. package/samples/extensions/logichooks/botium.json +1 -0
  8. package/samples/extensions/logichooks/custom/MyAsserter.js +3 -3
  9. package/src/Capabilities.js +2 -1
  10. package/src/containers/PluginConnectorContainer.js +8 -0
  11. package/src/containers/plugins/SimpleRestContainer.js +17 -9
  12. package/src/containers/plugins/index.js +29 -41
  13. package/src/helpers/HookUtils.js +32 -68
  14. package/src/scripting/Convo.js +17 -5
  15. package/src/scripting/ScriptingMemory.js +0 -24
  16. package/src/scripting/logichook/LogicHookConsts.js +5 -1
  17. package/src/scripting/logichook/LogicHookUtils.js +27 -47
  18. package/src/scripting/logichook/asserter/ButtonsAsserter.js +5 -5
  19. package/src/scripting/logichook/logichooks/ConditionalBusinessHoursLogicHook.js +56 -0
  20. package/src/scripting/logichook/logichooks/ConditionalCapabilityValueBasedLogicHook.js +37 -0
  21. package/src/scripting/logichook/logichooks/ConditionalJsonPathBasedLogicHook.js +31 -0
  22. package/src/scripting/logichook/logichooks/ConditionalTimeBasedLogicHook.js +46 -0
  23. package/test/compiler/precompilerscript.spec.js +24 -26
  24. package/test/connectors/pluginconnectorcontainer.spec.js +60 -0
  25. package/test/connectors/simplerest.spec.js +24 -27
  26. package/test/convo/fillAndApplyScriptingMemory.spec.js +1 -47
  27. package/test/hooks/customhooks.spec.js +3 -25
  28. package/test/logichooks/hookfromsrc.spec.js +13 -3
  29. package/test/plugins/plugins.spec.js +29 -2
  30. package/test/scripting/logichooks/CustomConditionalLogicHook.js +21 -0
  31. package/test/scripting/logichooks/conditionalStepBusinessHoursLogicHook.spec.js +130 -0
  32. package/test/scripting/logichooks/conditionalStepCapabilityValueBasedLogicHook.spec.js +35 -0
  33. package/test/scripting/logichooks/conditionalStepJsonPathBasedLogicHook.spec.js +35 -0
  34. package/test/scripting/logichooks/conditionalStepTimeBasedLogicHook.spec.js +91 -0
  35. package/test/scripting/logichooks/convos/conditional_steps.convo.txt +12 -0
  36. package/test/scripting/logichooks/convos/conditional_steps_business_hours.convo.txt +16 -0
  37. package/test/scripting/logichooks/convos/conditional_steps_cap_value_based.convo.txt +12 -0
  38. package/test/scripting/logichooks/convos/conditional_steps_followed_by_bot_msg.convo.txt +15 -0
  39. package/test/scripting/logichooks/convos/conditional_steps_followed_by_me.convo.txt +18 -0
  40. package/test/scripting/logichooks/convos/conditional_steps_json_path_based.convo.txt.convo.txt +12 -0
  41. package/test/scripting/logichooks/convos/conditional_steps_multiple_condition_groups.convo.txt +20 -0
  42. package/test/scripting/logichooks/convos/conditional_steps_multiple_condition_groups_no_assertion.convo.txt +20 -0
  43. package/test/scripting/logichooks/convos/conditional_steps_time_based.convo.txt +12 -0
  44. package/test/scripting/logichooks/customConditionalStepLogicHook.spec.js +105 -0
  45. package/test/scripting/scriptingProvider.spec.js +1 -1
  46. package/test/security/allowUnsafe.spec.js +20 -129
  47. package/LICENSES-3RDPARTY.txt +0 -6450
  48. package/test/scripting/asserters/convos/customembeddedasserterwithhugo.convo.txt +0 -7
  49. package/test/scripting/asserters/convos/customembeddedasserterwithouthugo.convo.txt +0 -7
  50. package/test/scripting/asserters/customEmbeddedAsserter.json +0 -14
  51. package/test/scripting/asserters/customEmbeddedAsserter.spec.js +0 -55
  52. package/test/scripting/logichooks/convos/custom_embedded.convo.txt +0 -8
  53. package/test/scripting/logichooks/convos/custom_embedded_skip.convo.txt +0 -11
  54. package/test/scripting/logichooks/convos/custom_embedded_skip_followed_by_me.convo.txt +0 -11
  55. package/test/scripting/logichooks/convos/custom_embedded_skip_followed_by_nothing.convo.txt +0 -8
  56. package/test/scripting/logichooks/customEmbedded.json +0 -14
  57. package/test/scripting/logichooks/customEmbedded.spec.js +0 -44
  58. package/test/scripting/logichooks/customEmbeddedSkip.json +0 -14
  59. package/test/scripting/logichooks/customEmbeddedSkip.spec.js +0 -58
  60. package/test/security/convos/withscriptingmemoryfunction.convo.txt +0 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "botium-core",
3
- "version": "1.13.18",
3
+ "version": "1.14.0",
4
4
  "description": "The Selenium for Chatbots",
5
5
  "main": "index.js",
6
6
  "module": "dist/botium-es.js",
@@ -18,7 +18,6 @@
18
18
  "link": "npm link botium-connector-dialogflow botium-connector-webdriverio botium-connector-directline3 botium-connector-watson botium-connector-alexa-smapi botium-connector-echo",
19
19
  "test": "cross-env NODE_PATH=\"./test/plugins/plugindir/fromfolder:./test/plugins/plugindir/fromfile:./test/security/resources\" mocha \"./test/**/*.spec.js\"",
20
20
  "coverage:report": "nyc report --reporter=lcov npm test",
21
- "license-checker": "license-checker > LICENSES-3RDPARTY.txt",
22
21
  "update-dependencies": "npm-check-updates --reject globby,rollup -u --timeout 120000"
23
22
  },
24
23
  "repository": {
@@ -32,14 +31,13 @@
32
31
  },
33
32
  "homepage": "https://www.botium.ai",
34
33
  "dependencies": {
35
- "@babel/runtime": "^7.21.5",
34
+ "@babel/runtime": "^7.22.15",
36
35
  "async": "^3.2.4",
37
36
  "body-parser": "^1.20.2",
38
37
  "boolean": "^3.2.0",
39
38
  "bottleneck": "^2.19.5",
40
- "csv-parse": "^5.3.10",
39
+ "csv-parse": "^5.5.0",
41
40
  "debug": "^4.3.4",
42
- "esprima": "^4.0.1",
43
41
  "express": "^4.18.2",
44
42
  "globby": "11.0.4",
45
43
  "ioredis": "^5.3.2",
@@ -51,47 +49,45 @@
51
49
  "mime-types": "^2.1.35",
52
50
  "mkdirp": "^3.0.1",
53
51
  "moment": "^2.29.4",
52
+ "moment-timezone": "^0.5.43",
54
53
  "mustache": "^4.2.0",
55
54
  "promise-retry": "^2.0.1",
56
- "promise.allsettled": "^1.0.6",
55
+ "promise.allsettled": "^1.0.7",
57
56
  "randomatic": "^3.1.1",
58
57
  "request": "^2.88.2",
59
- "rimraf": "^5.0.0",
58
+ "rimraf": "^5.0.1",
60
59
  "sanitize-filename": "^1.6.3",
61
60
  "slugify": "^1.6.6",
62
- "socket.io": "^4.6.1",
63
- "socket.io-client": "^4.6.1",
61
+ "socket.io": "^4.7.2",
62
+ "socket.io-client": "^4.7.2",
64
63
  "socketio-auth": "^0.1.1",
65
64
  "swagger-jsdoc": "^6.2.8",
66
- "swagger-ui-express": "^4.6.3",
65
+ "swagger-ui-express": "^5.0.0",
67
66
  "uuid": "^9.0.0",
68
- "vm2": "^3.9.17",
69
67
  "word-error-rate": "0.0.7",
70
68
  "write-yaml": "^1.0.0",
71
69
  "xlsx": "^0.18.5",
72
70
  "xregexp": "^5.1.1",
73
- "yaml": "^2.2.2"
71
+ "yaml": "^2.3.2"
74
72
  },
75
73
  "devDependencies": {
76
- "@babel/core": "^7.21.8",
77
- "@babel/node": "^7.20.7",
78
- "@babel/plugin-transform-runtime": "^7.21.4",
79
- "@babel/preset-env": "^7.21.5",
80
- "chai": "^4.3.7",
74
+ "@babel/core": "^7.22.17",
75
+ "@babel/node": "^7.22.15",
76
+ "@babel/plugin-transform-runtime": "^7.22.15",
77
+ "@babel/preset-env": "^7.22.15",
78
+ "chai": "^4.3.8",
81
79
  "chai-as-promised": "^7.1.1",
82
80
  "cross-env": "^7.0.3",
83
- "eslint": "^8.40.0",
84
- "eslint-config-standard": "^17.0.0",
85
- "eslint-plugin-import": "^2.27.5",
81
+ "eslint": "^8.49.0",
82
+ "eslint-config-standard": "^17.1.0",
83
+ "eslint-plugin-import": "^2.28.1",
86
84
  "eslint-plugin-mocha": "^10.1.0",
87
- "eslint-plugin-n": "^15.7.0",
85
+ "eslint-plugin-n": "^16.1.0",
88
86
  "eslint-plugin-promise": "^6.1.1",
89
87
  "eslint-plugin-standard": "^4.1.0",
90
- "license-checker": "^25.0.1",
91
- "license-compatibility-checker": "^0.3.5",
92
88
  "mocha": "^10.2.0",
93
- "nock": "^13.3.1",
94
- "npm-check-updates": "^16.10.12",
89
+ "nock": "^13.3.3",
90
+ "npm-check-updates": "^16.13.3",
95
91
  "nyc": "^15.1.0",
96
92
  "rollup": "2.79.1",
97
93
  "rollup-plugin-babel": "^4.4.0",
@@ -7,6 +7,7 @@
7
7
  "WATSON_PASSWORD": "ZWDE5xo02sby",
8
8
  "WATSON_WORKSPACE_ID": "97513bc0-c581-4bec-ac9f-ea6a8ec308a9",
9
9
  "SCRIPTING_ENABLE_MEMORY": true,
10
+ "SAFEDIR": ".",
10
11
  "ASSERTERS": [
11
12
  {
12
13
  "ref": "DUMMY",
@@ -3,6 +3,7 @@
3
3
  "Capabilities": {
4
4
  "PROJECTNAME": "Botium Logichooks Sample",
5
5
  "CONTAINERMODE": "echo",
6
+ "SAFEDIR": ".",
6
7
  "ASSERTERS": [
7
8
  {
8
9
  "ref": "MYASSERTER",
@@ -6,17 +6,17 @@ module.exports = class MyAsserter {
6
6
  this.caps = caps
7
7
  }
8
8
 
9
- assertConvoBegin ({convo, container, args}) {
9
+ assertConvoBegin ({ convo, container, args }) {
10
10
  console.log(`MyAsserter assertConvoBegin: ${convo.header.name}`)
11
11
  return Promise.resolve()
12
12
  }
13
13
 
14
- assertConvoStep ({convo, convoStep, args, botMsg}) {
14
+ assertConvoStep ({ convo, convoStep, args, botMsg }) {
15
15
  console.log(`MyAsserter assertConvoStep, botMessage: ${utils.inspect(botMsg)} ...`)
16
16
  return Promise.resolve()
17
17
  }
18
18
 
19
- assertConvoEnd ({convo, container, transcript, args}) {
19
+ assertConvoEnd ({ convo, container, transcript, args }) {
20
20
  console.log(`MyAsserter assertConvoEnd ${convo.header.name}, transcript: ${utils.inspect(transcript)} ...`)
21
21
  return Promise.resolve()
22
22
  }
@@ -3,6 +3,7 @@ module.exports = {
3
3
  TESTSESSIONNAME: 'TESTSESSIONNAME',
4
4
  TESTCASENAME: 'TESTCASENAME',
5
5
  TEMPDIR: 'TEMPDIR',
6
+ SAFEDIR: 'SAFEDIR',
6
7
  CLEANUPTEMPDIR: 'CLEANUPTEMPDIR',
7
8
  WAITFORBOTTIMEOUT: 'WAITFORBOTTIMEOUT',
8
9
  CONTAINERMODE: 'CONTAINERMODE',
@@ -12,7 +13,7 @@ module.exports = {
12
13
  BOTIUMGRIDURL: 'BOTIUMGRIDURL',
13
14
  BOTIUMAPITOKEN: 'BOTIUMAPITOKEN',
14
15
  BOTIUMGRIDSLOT: 'BOTIUMGRIDSLOT',
15
- // Simple Reset Bot Settings
16
+ // Simple Rest Bot Settings
16
17
  SIMPLEREST_PING_URL: 'SIMPLEREST_PING_URL',
17
18
  SIMPLEREST_PING_VERB: 'SIMPLEREST_PING_VERB',
18
19
  SIMPLEREST_PING_BODY: 'SIMPLEREST_PING_BODY',
@@ -162,4 +162,12 @@ module.exports = class PluginConnectorContainer extends BaseContainer {
162
162
  return Promise.reject(new Error(`Clean - Botium plugin failed: ${util.inspect(err)}`))
163
163
  }
164
164
  }
165
+
166
+ GetMetaData () {
167
+ try {
168
+ return (this.pluginInstance.GetMetaData ? (this.pluginInstance.GetMetaData() || Promise.resolve()) : Promise.resolve())
169
+ } catch (err) {
170
+ return Promise.reject(new Error(`GetMetaData - Botium plugin failed: ${util.inspect(err)}`))
171
+ }
172
+ }
165
173
  }
@@ -336,24 +336,33 @@ module.exports = class SimpleRestContainer {
336
336
 
337
337
  const result = []
338
338
  if (isFromUser) {
339
- const _extractFrom = (root, jsonPaths) => {
340
- const flattened = []
339
+ const _extractFrom = (root, jsonPaths, acceptFn = null) => {
340
+ const result = []
341
341
  for (const jsonPath of jsonPaths) {
342
+ const jsonPathRes = []
342
343
  const rb = jp.query(root, jsonPath)
343
344
  if (_.isArray(rb)) {
344
- _.flattenDeep(rb).forEach(r => flattened.push(r))
345
- } else if (rb) {
346
- flattened.push(rb)
345
+ _.flattenDeep(rb).forEach(r => jsonPathRes.push(r))
346
+ } else {
347
+ jsonPathRes.push(rb)
348
+ }
349
+ if (acceptFn) {
350
+ result.push(...jsonPathRes.filter(r => acceptFn(root, jsonPath, r)))
351
+ } else {
352
+ result.push(...jsonPathRes)
347
353
  }
348
354
  }
349
- return flattened
355
+ return result
350
356
  }
351
357
 
352
358
  const jsonPathRoots = []
353
-
354
359
  const jsonPathsBody = getAllCapValues(Capabilities.SIMPLEREST_BODY_JSONPATH, this.caps)
355
360
  if (jsonPathsBody.length > 0) {
356
- jsonPathRoots.push(..._extractFrom(body, jsonPathsBody))
361
+ jsonPathRoots.push(..._extractFrom(body, jsonPathsBody, (root, jsonPath, r) => {
362
+ if (r && _.isObject(r)) return true
363
+ debug(`Ignoring result body from ${jsonPath} - not a querieable object (${util.inspect(r)})`)
364
+ return false
365
+ }))
357
366
  } else {
358
367
  jsonPathRoots.push(body)
359
368
  }
@@ -381,7 +390,6 @@ module.exports = class SimpleRestContainer {
381
390
  const jsonPathsButtonsPayload = getAllCapValues(`${capPrefix}_PAYLOAD_SUBJSONPATH`, this.caps)
382
391
 
383
392
  const retrievedButtons = []
384
-
385
393
  const responseButtons = _extractFrom(jsonPathButtonRoot, jsonPathsButtons)
386
394
  for (const responseButton of responseButtons) {
387
395
  const retrievedButton = {}
@@ -5,7 +5,6 @@ const debug = require('debug')('botium-connector-PluginConnectorContainer-helper
5
5
 
6
6
  const SimpleRestContainer = require('./SimpleRestContainer')
7
7
  const Capabilities = require('../../Capabilities')
8
- const { BotiumError } = require('../../scripting/BotiumError')
9
8
 
10
9
  const pluginResolver = (containermode) => {
11
10
  if (containermode === 'simplerest') {
@@ -59,23 +58,6 @@ const loadConnectorModule = (PluginClass, args) => {
59
58
 
60
59
  const tryLoadPlugin = (containermode, modulepath, args) => {
61
60
  const pluginLoaderSpec = modulepath || containermode
62
- const _checkUnsafe = (caps, mode, cause) => {
63
- if (!caps[Capabilities.SECURITY_ALLOW_UNSAFE]) {
64
- throw new BotiumError(
65
- `Security Error. Using unsafe connector mode "${mode}" is not allowed`,
66
- {
67
- type: 'security',
68
- subtype: 'allow unsafe',
69
- source: 'src/containers/plugins/index.js',
70
- cause: {
71
- SECURITY_ALLOW_UNSAFE: caps[Capabilities.SECURITY_ALLOW_UNSAFE],
72
- mode,
73
- ...cause
74
- }
75
- }
76
- )
77
- }
78
- }
79
61
 
80
62
  if (pluginResolver(pluginLoaderSpec)) {
81
63
  const pluginInstance = new (pluginResolver(pluginLoaderSpec))(args)
@@ -88,44 +70,50 @@ const tryLoadPlugin = (containermode, modulepath, args) => {
88
70
  return pluginInstance
89
71
  }
90
72
  const loadErr = []
73
+ const allowUnsafe = !!args.caps[Capabilities.SECURITY_ALLOW_UNSAFE]
91
74
 
92
75
  if (_.isString(pluginLoaderSpec)) {
93
- const tryLoadFile = path.resolve(process.cwd(), pluginLoaderSpec)
94
- if (fs.existsSync(tryLoadFile)) {
95
- _checkUnsafe(args.caps, 'Using work dir', { modulepath, containermode })
76
+ if (args.caps.SAFEDIR) {
77
+ const tryLoadFile = path.resolve(args.caps.SAFEDIR, pluginLoaderSpec)
78
+ if (tryLoadFile.startsWith(path.resolve(args.caps.SAFEDIR))) {
79
+ if (fs.existsSync(tryLoadFile)) {
80
+ try {
81
+ let plugin = require(tryLoadFile)
82
+ if (plugin.default) {
83
+ plugin = plugin.default
84
+ }
85
+ if (!plugin.PluginVersion || !plugin.PluginClass) {
86
+ loadErr.push(`Invalid Botium plugin loaded from ${tryLoadFile}, expected PluginVersion, PluginClass fields`)
87
+ } else {
88
+ const pluginInstance = loadConnectorModule(plugin.PluginClass, args)
89
+ debug(`Botium plugin loaded from ${tryLoadFile}`)
90
+ return pluginInstance
91
+ }
92
+ } catch (err) {
93
+ loadErr.push(`Loading Botium plugin from ${tryLoadFile} failed - ${err.message}`)
94
+ }
95
+ }
96
+ }
97
+ }
98
+
99
+ if (allowUnsafe) {
96
100
  try {
97
- let plugin = require(tryLoadFile)
101
+ let plugin = require(pluginLoaderSpec)
98
102
  if (plugin.default) {
99
103
  plugin = plugin.default
100
104
  }
101
105
  if (!plugin.PluginVersion || !plugin.PluginClass) {
102
- loadErr.push(`Invalid Botium plugin loaded from ${tryLoadFile}, expected PluginVersion, PluginClass fields`)
106
+ loadErr.push(`Invalid Botium plugin loaded from ${pluginLoaderSpec}, expected PluginVersion, PluginClass fields`)
103
107
  } else {
104
108
  const pluginInstance = loadConnectorModule(plugin.PluginClass, args)
105
- debug(`Botium plugin loaded from ${tryLoadFile}`)
109
+ debug(`Botium plugin loaded from ${pluginLoaderSpec}. Plugin version is ${getModuleVersionSafe(pluginLoaderSpec)}`)
106
110
  return pluginInstance
107
111
  }
108
112
  } catch (err) {
109
- loadErr.push(`Loading Botium plugin from ${tryLoadFile} failed - ${err.message}`)
113
+ loadErr.push(`Loading Botium plugin from ${pluginLoaderSpec} failed - ${err.message}`)
110
114
  }
111
115
  }
112
116
 
113
- try {
114
- let plugin = require(pluginLoaderSpec)
115
- if (plugin.default) {
116
- plugin = plugin.default
117
- }
118
- if (!plugin.PluginVersion || !plugin.PluginClass) {
119
- loadErr.push(`Invalid Botium plugin loaded from ${pluginLoaderSpec}, expected PluginVersion, PluginClass fields`)
120
- } else {
121
- const pluginInstance = loadConnectorModule(plugin.PluginClass, args)
122
- debug(`Botium plugin loaded from ${pluginLoaderSpec}. Plugin version is ${getModuleVersionSafe(pluginLoaderSpec)}`)
123
- return pluginInstance
124
- }
125
- } catch (err) {
126
- loadErr.push(`Loading Botium plugin from ${pluginLoaderSpec} failed - ${err.message}`)
127
- }
128
-
129
117
  const tryLoadPackage = `botium-connector-${pluginLoaderSpec}`
130
118
  try {
131
119
  let plugin = require(tryLoadPackage)
@@ -1,48 +1,28 @@
1
1
  const util = require('util')
2
2
  const path = require('path')
3
3
  const fs = require('fs')
4
- const { NodeVM } = require('vm2')
5
- const esprima = require('esprima')
6
4
  const _ = require('lodash')
7
5
  const debug = require('debug')('botium-core-HookUtils')
8
6
 
9
7
  const Capabilities = require('../Capabilities')
10
- const { BotiumError } = require('../scripting/BotiumError')
11
8
 
12
- const executeHook = async (caps, hook, args) => {
13
- return executeHookSync(caps, hook, args)
9
+ const executeHook = async (caps, hook, ...args) => {
10
+ return executeHookSync(caps, hook, ...args)
14
11
  }
15
12
 
16
- const executeHookSync = (caps, hook, args) => {
13
+ const executeHookSync = (caps, hook, ...args) => {
17
14
  if (!hook) {
18
15
  return
19
16
  }
20
17
 
21
18
  if (_.isFunction(hook)) {
22
19
  try {
23
- return hook(args)
20
+ return hook(...args)
24
21
  } catch (err) {
25
22
  throw new Error(`Calling Hook function failed: ${err.message}`)
26
23
  }
27
24
  }
28
25
 
29
- if (_.isString(hook)) {
30
- try {
31
- const vm = new NodeVM({
32
- eval: false,
33
- require: false,
34
- sandbox: args
35
- })
36
- const r = vm.run(hook)
37
- if (_.isFunction(r)) {
38
- return r(args)
39
- } else {
40
- return r
41
- }
42
- } catch (err) {
43
- throw new Error(`Calling Hook Javascript code failed: ${err.message}`)
44
- }
45
- }
46
26
  throw new Error(`Unknown hook ${typeof hook}`)
47
27
  }
48
28
 
@@ -58,56 +38,40 @@ const getHook = (caps, data) => {
58
38
  }
59
39
 
60
40
  if (_.isString(data)) {
61
- let resultWithRequire
62
- let tryLoadFile = path.resolve(process.cwd(), data)
63
- if (fs.existsSync(tryLoadFile)) {
64
- try {
65
- resultWithRequire = require(tryLoadFile)
66
- } catch (err) {
67
- }
68
- } else {
69
- tryLoadFile = data
70
- try {
71
- resultWithRequire = require(data)
72
- } catch (err) {
73
- }
74
- }
75
-
76
- if (resultWithRequire) {
77
- if (!allowUnsafe) {
78
- throw new BotiumError(
79
- 'Security Error. Using unsafe custom hook with require is not allowed',
80
- {
81
- type: 'security',
82
- subtype: 'allow unsafe',
83
- source: path.basename(__filename),
84
- cause: {
85
- SECURITY_ALLOW_UNSAFE: caps[Capabilities.SECURITY_ALLOW_UNSAFE],
86
- hookData: data
41
+ if (caps.SAFEDIR) {
42
+ const tryLoadFile = path.resolve(caps.SAFEDIR, data)
43
+ if (tryLoadFile.startsWith(path.resolve(caps.SAFEDIR))) {
44
+ if (fs.existsSync(tryLoadFile)) {
45
+ try {
46
+ const resultWithRequire = require(tryLoadFile)
47
+ if (_.isFunction(resultWithRequire)) {
48
+ debug(`found hook, type: safedir, in ${tryLoadFile}`)
49
+ return resultWithRequire
50
+ } else {
51
+ throw new Error(`Expected function from hook specification "${util.inspect(data)}", got: "${util.inspect(resultWithRequire)}"`)
87
52
  }
53
+ } catch (err) {
54
+ debug(`Failed loading hook, type: safedir, from ${tryLoadFile} failed: ${err.message || err}`)
88
55
  }
89
- )
90
- }
91
-
92
- if (_.isFunction(resultWithRequire)) {
93
- debug(`found hook, type: require, in ${tryLoadFile}`)
94
- return resultWithRequire
95
- } else {
96
- throw new Error(`Cant load hook ${tryLoadFile} because it is not a function`)
56
+ }
97
57
  }
98
58
  }
99
-
100
- try {
101
- esprima.parseScript(data)
102
- } catch (err) {
103
- throw new Error(`Cant load hook, syntax is not valid - ${util.inspect(err)}`)
59
+ if (allowUnsafe || data.startsWith('botium-')) {
60
+ const tryLoadFile = data
61
+ try {
62
+ const resultWithRequire = require(tryLoadFile)
63
+ if (_.isFunction(resultWithRequire)) {
64
+ debug(`found hook, type: require, in ${tryLoadFile}`)
65
+ return resultWithRequire
66
+ } else {
67
+ throw new Error(`Expected function from hook specification "${util.inspect(data)}", got: "${util.inspect(resultWithRequire)}"`)
68
+ }
69
+ } catch (err) {
70
+ debug(`Failed loading hook, type: require, from ${tryLoadFile} failed: ${err.message || err}`)
71
+ }
104
72
  }
105
-
106
- debug('Found hook, type: JavaScript as String')
107
- return data
108
73
  }
109
-
110
- throw new Error(`Not valid hook ${util.inspect(data)}`)
74
+ throw new Error(`Hook specification "${util.inspect(data)}" invalid: no loader available`)
111
75
  }
112
76
 
113
77
  module.exports = {
@@ -356,6 +356,7 @@ class Convo {
356
356
  throw failErr
357
357
  }
358
358
  } else if (convoStep.sender === 'bot') {
359
+ const previousWaitForBotSays = waitForBotSays
359
360
  if (waitForBotSays) {
360
361
  botMsg = null
361
362
  } else {
@@ -404,13 +405,24 @@ class Convo {
404
405
  throw failErr
405
406
  }
406
407
 
407
- if (convoStep.skip === true) {
408
- skipTranscriptStep = true
408
+ if (convoStep.conditional) {
409
409
  const nextConvoStep = this.conversation[i + 1]
410
- if (nextConvoStep && nextConvoStep.sender === 'bot') {
411
- waitForBotSays = false
410
+
411
+ if (!previousWaitForBotSays) {
412
+ skipTranscriptStep = true
413
+ }
414
+ waitForBotSays = false
415
+ if (!nextConvoStep || nextConvoStep.sender !== 'bot' || !nextConvoStep.logicHooks || !nextConvoStep.logicHooks.some(lh => lh.name.toUpperCase().startsWith('CONDITIONAL_STEP'))) {
416
+ waitForBotSays = true
417
+ } else {
418
+ const conditionalLogicHook = convoStep.logicHooks.find(lh => lh.name.startsWith('CONDITIONAL_STEP'))
419
+ const nextConditionalLogicHook = nextConvoStep.logicHooks.find(lh => lh.name.startsWith('CONDITIONAL_STEP'))
420
+ waitForBotSays = conditionalLogicHook.args[1] !== nextConditionalLogicHook.args[1]
421
+ }
422
+
423
+ if (convoStep.conditional.skip) {
424
+ continue
412
425
  }
413
- continue
414
426
  }
415
427
 
416
428
  if (!botMsg || (!botMsg.messageText && !botMsg.media && !botMsg.buttons && !botMsg.cards && !botMsg.sourceData && !botMsg.nlp)) {
@@ -3,7 +3,6 @@ const debug = require('debug')('botium-core-ScriptingMemory')
3
3
  const randomize = require('randomatic')
4
4
  const { v1: uuidv1 } = require('uuid')
5
5
  const moment = require('moment')
6
- const { NodeVM } = require('vm2')
7
6
  const _ = require('lodash')
8
7
  const path = require('path')
9
8
  const jp = require('jsonpath')
@@ -181,29 +180,6 @@ const SCRIPTING_FUNCTIONS_RAW = {
181
180
  else return ''
182
181
  },
183
182
  numberOfArguments: 1
184
- },
185
-
186
- $func: {
187
- handler: (caps, code) => {
188
- if (code == null) {
189
- throw Error('func function used without args!')
190
- }
191
- try {
192
- const vm = new NodeVM({
193
- eval: false,
194
- require: false,
195
- env: caps[Capabilities.SECURITY_ALLOW_UNSAFE] ? process.env : {},
196
- sandbox: {
197
- caps,
198
- moment
199
- }
200
- })
201
- return vm.run(`module.exports = (${code})`)
202
- } catch (err) {
203
- throw Error(`func function execution failed - ${err}`)
204
- }
205
- },
206
- numberOfArguments: 1
207
183
  }
208
184
  }
209
185
 
@@ -57,7 +57,11 @@ module.exports = {
57
57
  { name: 'ASSIGN_SCRIPTING_MEMORY', className: 'AssignScriptingMemoryLogicHook' },
58
58
  { name: 'UPDATE_CUSTOM', className: 'UpdateCustomLogicHook' },
59
59
  { name: 'SKIP_BOT_UNCONSUMED', className: 'ClearQueueLogicHook' },
60
- { name: LOGIC_HOOK_INCLUDE, className: 'IncludeLogicHook' }
60
+ { name: LOGIC_HOOK_INCLUDE, className: 'IncludeLogicHook' },
61
+ { name: 'CONDITIONAL_STEP_TIME_BASED', className: 'ConditionalTimeBasedLogicHook' },
62
+ { name: 'CONDITIONAL_STEP_BUSINESS_HOURS', className: 'ConditionalBusinessHoursLogicHook' },
63
+ { name: 'CONDITIONAL_STEP_CAPABILITY_VALUE_BASED', className: 'ConditionalCapabilityValueBasedLogicHook' },
64
+ { name: 'CONDITIONAL_STEP_JSON_PATH_BASED', className: 'ConditionalJsonPathBasedLogicHook.js' }
61
65
  ],
62
66
  DEFAULT_USER_INPUTS: [
63
67
  { name: 'BUTTON', className: 'ButtonInput' },