botium-core 1.12.6 → 1.13.2
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.
- package/dist/botium-cjs.js +260 -158
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +279 -177
- package/dist/botium-es.js.map +1 -1
- package/package.json +24 -24
- package/src/Capabilities.js +7 -0
- package/src/containers/plugins/SimpleRestContainer.js +3 -0
- package/src/helpers/RetryHelper.js +13 -7
- package/src/scripting/CompilerCsv.js +150 -102
- package/src/scripting/Convo.js +20 -0
- package/src/scripting/ScriptingProvider.js +5 -1
- package/src/scripting/logichook/LogicHookUtils.js +1 -1
- package/test/compiler/compilercsv.spec.js +363 -12
- package/test/compiler/convos/csv/utterances_liveperson.csv +108 -0
- package/test/compiler/convos/csv/utterances_multicolumn3col.csv +3 -0
- package/test/compiler/convos/csv/utterances_multicolumn5col.csv +3 -0
- package/test/compiler/convos/csv/utterances_singlecolumn.csv +3 -0
- package/test/compiler/convos/csv/utterances_variable_row_len.csv +3 -0
- package/test/convo/retryconvo.spec.js +134 -0
- package/test/logichooks/hookfromsrc.spec.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "botium-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.2",
|
|
4
4
|
"description": "The Selenium for Chatbots",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "dist/botium-es.js",
|
|
@@ -32,25 +32,25 @@
|
|
|
32
32
|
},
|
|
33
33
|
"homepage": "https://www.botium.ai",
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@babel/runtime": "^7.
|
|
36
|
-
"async": "^3.2.
|
|
35
|
+
"@babel/runtime": "^7.18.6",
|
|
36
|
+
"async": "^3.2.4",
|
|
37
37
|
"body-parser": "^1.20.0",
|
|
38
38
|
"boolean": "^3.2.0",
|
|
39
39
|
"bottleneck": "^2.19.5",
|
|
40
|
-
"csv-parse": "^5.0
|
|
40
|
+
"csv-parse": "^5.3.0",
|
|
41
41
|
"debug": "^4.3.4",
|
|
42
42
|
"esprima": "^4.0.1",
|
|
43
|
-
"express": "^4.
|
|
43
|
+
"express": "^4.18.1",
|
|
44
44
|
"globby": "11.0.4",
|
|
45
|
-
"ioredis": "^5.0
|
|
45
|
+
"ioredis": "^5.1.0",
|
|
46
46
|
"is-class": "^0.0.9",
|
|
47
47
|
"is-json": "^2.0.1",
|
|
48
48
|
"jsonpath": "^1.1.1",
|
|
49
49
|
"lodash": "^4.17.21",
|
|
50
|
-
"markdown-it": "^
|
|
50
|
+
"markdown-it": "^13.0.1",
|
|
51
51
|
"mime-types": "^2.1.35",
|
|
52
52
|
"mkdirp": "^1.0.4",
|
|
53
|
-
"moment": "^2.29.
|
|
53
|
+
"moment": "^2.29.4",
|
|
54
54
|
"mustache": "^4.2.0",
|
|
55
55
|
"promise-retry": "^2.0.1",
|
|
56
56
|
"promise.allsettled": "^1.0.5",
|
|
@@ -59,39 +59,39 @@
|
|
|
59
59
|
"rimraf": "^3.0.2",
|
|
60
60
|
"sanitize-filename": "^1.6.3",
|
|
61
61
|
"slugify": "^1.6.5",
|
|
62
|
-
"socket.io": "^4.
|
|
63
|
-
"socket.io-client": "^4.
|
|
62
|
+
"socket.io": "^4.5.1",
|
|
63
|
+
"socket.io-client": "^4.5.1",
|
|
64
64
|
"socketio-auth": "^0.1.1",
|
|
65
65
|
"swagger-jsdoc": "^6.2.1",
|
|
66
|
-
"swagger-ui-express": "^4.
|
|
66
|
+
"swagger-ui-express": "^4.4.0",
|
|
67
67
|
"uuid": "^8.3.2",
|
|
68
|
-
"vm2": "^3.9.
|
|
68
|
+
"vm2": "^3.9.10",
|
|
69
69
|
"write-yaml": "^1.0.0",
|
|
70
70
|
"xlsx": "^0.18.5",
|
|
71
|
-
"xregexp": "^5.1.
|
|
72
|
-
"yaml": "^2.
|
|
71
|
+
"xregexp": "^5.1.1",
|
|
72
|
+
"yaml": "^2.1.1"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@babel/core": "^7.
|
|
76
|
-
"@babel/node": "^7.
|
|
77
|
-
"@babel/plugin-transform-runtime": "^7.
|
|
78
|
-
"@babel/preset-env": "^7.
|
|
75
|
+
"@babel/core": "^7.18.6",
|
|
76
|
+
"@babel/node": "^7.18.6",
|
|
77
|
+
"@babel/plugin-transform-runtime": "^7.18.6",
|
|
78
|
+
"@babel/preset-env": "^7.18.6",
|
|
79
79
|
"chai": "^4.3.6",
|
|
80
80
|
"chai-as-promised": "^7.1.1",
|
|
81
81
|
"cross-env": "^7.0.3",
|
|
82
|
-
"eslint": "^8.
|
|
82
|
+
"eslint": "^8.19.0",
|
|
83
83
|
"eslint-config-standard": "^17.0.0",
|
|
84
84
|
"eslint-plugin-import": "^2.26.0",
|
|
85
|
-
"eslint-plugin-n": "^15.
|
|
85
|
+
"eslint-plugin-n": "^15.2.4",
|
|
86
86
|
"eslint-plugin-promise": "^6.0.0",
|
|
87
87
|
"eslint-plugin-standard": "^4.1.0",
|
|
88
88
|
"license-checker": "^25.0.1",
|
|
89
89
|
"license-compatibility-checker": "^0.3.5",
|
|
90
|
-
"mocha": "^
|
|
91
|
-
"nock": "^13.2.
|
|
92
|
-
"npm-check-updates": "^
|
|
90
|
+
"mocha": "^10.0.0",
|
|
91
|
+
"nock": "^13.2.8",
|
|
92
|
+
"npm-check-updates": "^15.2.6",
|
|
93
93
|
"nyc": "^15.1.0",
|
|
94
|
-
"rollup": "^2.
|
|
94
|
+
"rollup": "^2.76.0",
|
|
95
95
|
"rollup-plugin-babel": "^4.4.0",
|
|
96
96
|
"rollup-plugin-commonjs": "^10.1.0",
|
|
97
97
|
"rollup-plugin-json": "^4.0.0",
|
package/src/Capabilities.js
CHANGED
|
@@ -105,6 +105,10 @@ module.exports = {
|
|
|
105
105
|
SCRIPTING_XLSX_SHEETNAMES_PCONVOS: 'SCRIPTING_XLSX_SHEETNAMES_PCONVOS',
|
|
106
106
|
SCRIPTING_XLSX_SHEETNAMES_UTTERANCES: 'SCRIPTING_XLSX_SHEETNAMES_UTTERANCES',
|
|
107
107
|
SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY: 'SCRIPTING_XLSX_SHEETNAMES_SCRIPTING_MEMORY',
|
|
108
|
+
// hidden capability. All newly in Box created testsets will have this as true. CsvCompiler
|
|
109
|
+
// - throws less error (Box reads csv files as utterances, and convo. Compiler cant throw exception if a file is correct, but box tries to load it with incorrect script type)
|
|
110
|
+
// 4 or more colums are compiled just as utterances.
|
|
111
|
+
SCRIPTING_CSV_LEGACY_MODE_OFF: 'SCRIPTING_CSV_LEGACY_MODE_OFF',
|
|
108
112
|
SCRIPTING_CSV_DELIMITER: 'SCRIPTING_CSV_DELIMITER',
|
|
109
113
|
SCRIPTING_CSV_SKIP_HEADER: 'SCRIPTING_CSV_SKIP_HEADER',
|
|
110
114
|
SCRIPTING_CSV_QUOTE: 'SCRIPTING_CSV_QUOTE',
|
|
@@ -114,6 +118,9 @@ module.exports = {
|
|
|
114
118
|
SCRIPTING_CSV_MULTIROW_COLUMN_TEXT: 'SCRIPTING_CSV_MULTIROW_COLUMN_TEXT',
|
|
115
119
|
SCRIPTING_CSV_QA_COLUMN_QUESTION: 'SCRIPTING_CSV_QA_COLUMN_QUESTION',
|
|
116
120
|
SCRIPTING_CSV_QA_COLUMN_ANSWER: 'SCRIPTING_CSV_QA_COLUMN_ANSWER',
|
|
121
|
+
SCRIPTING_CSV_UTTERANCE_STARTROW: 'SCRIPTING_CSV_UTTERANCE_STARTROW',
|
|
122
|
+
SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER: 'SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER',
|
|
123
|
+
SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY: 'SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY',
|
|
117
124
|
SCRIPTING_NORMALIZE_TEXT: 'SCRIPTING_NORMALIZE_TEXT',
|
|
118
125
|
SCRIPTING_ENABLE_MEMORY: 'SCRIPTING_ENABLE_MEMORY',
|
|
119
126
|
SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS: 'SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS',
|
|
@@ -520,6 +520,9 @@ module.exports = class SimpleRestContainer {
|
|
|
520
520
|
try {
|
|
521
521
|
requestOptions.body = this._getMustachedCap(Capabilities.SIMPLEREST_BODY_TEMPLATE, !bodyRaw)
|
|
522
522
|
requestOptions.json = !bodyRaw
|
|
523
|
+
if (requestOptions.json && (!requestOptions.body || Object.keys(requestOptions.body).length === 0)) {
|
|
524
|
+
debug(`warning: requestOptions.body content seems to be empty - ${requestOptions.body} - capability: "${this.caps[Capabilities.SIMPLEREST_BODY_TEMPLATE]}"`)
|
|
525
|
+
}
|
|
523
526
|
} catch (err) {
|
|
524
527
|
throw new Error(`composing body from SIMPLEREST_BODY_TEMPLATE failed (${err.message})`)
|
|
525
528
|
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const _ = require('lodash')
|
|
3
|
+
const debug = require('debug')('botium-core-RetryHelper')
|
|
3
4
|
|
|
4
5
|
module.exports = class RetryHelper {
|
|
5
|
-
constructor (caps, section) {
|
|
6
|
-
this.retrySettings = {
|
|
7
|
-
retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || 1,
|
|
8
|
-
factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || 1,
|
|
9
|
-
minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || 1000
|
|
10
|
-
}
|
|
6
|
+
constructor (caps, section, options = {}) {
|
|
11
7
|
this.retryErrorPatterns = []
|
|
12
8
|
const onErrorRegexp = caps[`RETRY_${section.toUpperCase()}_ONERROR_REGEXP`] || []
|
|
13
9
|
if (onErrorRegexp) {
|
|
@@ -22,10 +18,20 @@ module.exports = class RetryHelper {
|
|
|
22
18
|
this.retryErrorPatterns.push(onErrorRegexp)
|
|
23
19
|
}
|
|
24
20
|
}
|
|
21
|
+
|
|
22
|
+
// to turn on retries, NUMRETRIES or ONERROR_REGEXP has to be set
|
|
23
|
+
this.retrySettings = {
|
|
24
|
+
retries: caps[`RETRY_${section.toUpperCase()}_NUMRETRIES`] || (!_.isNil(options.numRetries) ? options.numRetries : (this.retryErrorPatterns.length === 0) ? 0 : 1),
|
|
25
|
+
factor: caps[`RETRY_${section.toUpperCase()}_FACTOR`] || (_.isNil(options.factor) ? 1 : options.factor),
|
|
26
|
+
minTimeout: caps[`RETRY_${section.toUpperCase()}_MINTIMEOUT`] || (_.isNil(options.minTimeout) ? 1000 : options.minTimeout)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
debug(`Retry for ${section} is ${this.retrySettings.retries > 0 ? 'enabled' : 'disabled'}. Settings: ${JSON.stringify(this.retrySettings)} Patterns: ${JSON.stringify(this.retryErrorPatterns.map(r => r.toString()))}`)
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
shouldRetry (err) {
|
|
28
|
-
if (!err
|
|
33
|
+
if (!err) return false
|
|
34
|
+
if (this.retryErrorPatterns.length === 0) return true
|
|
29
35
|
const errString = util.inspect(err)
|
|
30
36
|
for (const re of this.retryErrorPatterns) {
|
|
31
37
|
if (errString.match(re)) return true
|
|
@@ -47,6 +47,7 @@ module.exports = class CompilerCsv extends CompilerBase {
|
|
|
47
47
|
if (scriptData.length === 0) {
|
|
48
48
|
return []
|
|
49
49
|
}
|
|
50
|
+
const legacyModeOn = !this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_LEGACY_MODE_OFF, false)
|
|
50
51
|
|
|
51
52
|
let delimiter = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_DELIMITER)
|
|
52
53
|
if (!delimiter) {
|
|
@@ -71,7 +72,8 @@ module.exports = class CompilerCsv extends CompilerBase {
|
|
|
71
72
|
delimiter,
|
|
72
73
|
escape: this.caps[Capabilities.SCRIPTING_CSV_ESCAPE],
|
|
73
74
|
quote: this.caps[Capabilities.SCRIPTING_CSV_QUOTE],
|
|
74
|
-
columns: false
|
|
75
|
+
columns: false,
|
|
76
|
+
relax_column_count: true
|
|
75
77
|
})
|
|
76
78
|
} catch (err) {
|
|
77
79
|
throw new Error(`Invalid CSV: ${err.message || err}`)
|
|
@@ -79,123 +81,169 @@ module.exports = class CompilerCsv extends CompilerBase {
|
|
|
79
81
|
if (rows.length === 0) {
|
|
80
82
|
return []
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
const columnCount = rows[0].length
|
|
85
|
+
debug(`Legacy mode ${legacyModeOn ? 'on' : 'off'} rows ${rows.length} columns ${columnCount}`)
|
|
86
|
+
|
|
87
|
+
if ((scriptType === Constants.SCRIPTING_TYPE_CONVO || scriptType === Constants.SCRIPTING_TYPE_PCONVO)) {
|
|
88
|
+
if (columnCount === 1 || (!legacyModeOn && columnCount > 3)) {
|
|
89
|
+
debug(`Invalid column count '${columnCount}' in convo mode`)
|
|
90
|
+
return []
|
|
91
|
+
}
|
|
92
|
+
let header = null
|
|
93
|
+
if (rows.length > 0 && this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER]) {
|
|
94
|
+
header = rows[0]
|
|
95
|
+
rows = rows.slice(1)
|
|
96
|
+
}
|
|
97
|
+
if (rows.length === 0) {
|
|
98
|
+
debug('Datarows not found in convo mode')
|
|
89
99
|
return []
|
|
90
100
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (scriptType !== Constants.SCRIPTING_TYPE_CONVO && scriptType !== Constants.SCRIPTING_TYPE_PCONVO) {
|
|
94
|
-
return []
|
|
95
|
-
}
|
|
96
|
-
let header = null
|
|
97
|
-
if (rows.length > 0 && this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER]) {
|
|
98
|
-
header = rows[0]
|
|
99
|
-
rows = rows.slice(1)
|
|
100
|
-
}
|
|
101
|
-
if (rows.length === 0) {
|
|
102
|
-
return []
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const lineNumberBase = this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER] ? 2 : 1
|
|
106
|
-
if (rows[0].length === 2) {
|
|
107
|
-
debug('Found 2-column CSV file, treating it as question/answer file')
|
|
108
101
|
|
|
109
|
-
|
|
110
|
-
|
|
102
|
+
const lineNumberBase = this.caps[Capabilities.SCRIPTING_CSV_SKIP_HEADER] ? 2 : 1
|
|
103
|
+
if (columnCount === 2) {
|
|
104
|
+
let colQuestion = DEFAULT_QA_COLUMN_QUESTION
|
|
105
|
+
let colAnswer = DEFAULT_QA_COLUMN_ANSWER
|
|
111
106
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
if (header) {
|
|
108
|
+
if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION] !== undefined) {
|
|
109
|
+
colQuestion = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_QUESTION])
|
|
110
|
+
}
|
|
111
|
+
if (this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER] !== undefined) {
|
|
112
|
+
colAnswer = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_QA_COLUMN_ANSWER])
|
|
113
|
+
}
|
|
115
114
|
}
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
|
|
116
|
+
const convos = rows.map((row, i) => new Convo(this.context, {
|
|
117
|
+
header: {
|
|
118
|
+
name: `L${i + lineNumberBase}`
|
|
119
|
+
},
|
|
120
|
+
conversation: [
|
|
121
|
+
Object.assign({},
|
|
122
|
+
linesToConvoStep(
|
|
123
|
+
[row[colQuestion]],
|
|
124
|
+
'me',
|
|
125
|
+
this.context,
|
|
126
|
+
undefined,
|
|
127
|
+
true
|
|
128
|
+
), { stepTag: `L${i + lineNumberBase}-Question` }),
|
|
129
|
+
Object.assign({},
|
|
130
|
+
linesToConvoStep(
|
|
131
|
+
[row[colAnswer]],
|
|
132
|
+
'bot',
|
|
133
|
+
this.context,
|
|
134
|
+
undefined,
|
|
135
|
+
true
|
|
136
|
+
), { stepTag: `L${i + lineNumberBase}-Answer` })
|
|
137
|
+
]
|
|
138
|
+
}))
|
|
139
|
+
if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
|
|
140
|
+
this.context.AddConvos(convos)
|
|
141
|
+
} else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
|
|
142
|
+
this.context.AddPartialConvos(convos)
|
|
118
143
|
}
|
|
144
|
+
debug(`Found 2-column CSV file, treating it as question/answer file, extracted ${convos.length} convos`)
|
|
145
|
+
return convos
|
|
119
146
|
}
|
|
120
147
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
conversation: [
|
|
126
|
-
Object.assign({},
|
|
127
|
-
linesToConvoStep(
|
|
128
|
-
[row[colQuestion]],
|
|
129
|
-
'me',
|
|
130
|
-
this.context,
|
|
131
|
-
undefined,
|
|
132
|
-
true
|
|
133
|
-
), { stepTag: `L${i + lineNumberBase}-Question` }),
|
|
134
|
-
Object.assign({},
|
|
135
|
-
linesToConvoStep(
|
|
136
|
-
[row[colAnswer]],
|
|
137
|
-
'bot',
|
|
138
|
-
this.context,
|
|
139
|
-
undefined,
|
|
140
|
-
true
|
|
141
|
-
), { stepTag: `L${i + lineNumberBase}-Answer` })
|
|
142
|
-
]
|
|
143
|
-
}))
|
|
144
|
-
if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
|
|
145
|
-
this.context.AddConvos(convos)
|
|
146
|
-
} else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
|
|
147
|
-
this.context.AddPartialConvos(convos)
|
|
148
|
-
}
|
|
149
|
-
return convos
|
|
150
|
-
}
|
|
148
|
+
if (columnCount >= 3) {
|
|
149
|
+
let colConversationId = DEFAULT_MULTIROW_COLUMN_CONVERSATION
|
|
150
|
+
let colSender = DEFAULT_MULTIROW_COLUMN_SENDER
|
|
151
|
+
let colText = DEFAULT_MULTIROW_COLUMN_TEXT
|
|
151
152
|
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
if (header) {
|
|
154
|
+
if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID] !== undefined) {
|
|
155
|
+
colConversationId = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_CONVERSATION_ID])
|
|
156
|
+
}
|
|
157
|
+
if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER] !== undefined) {
|
|
158
|
+
colSender = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_SENDER])
|
|
159
|
+
}
|
|
160
|
+
if (this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT] !== undefined) {
|
|
161
|
+
colText = _findColIndex(header, this.caps[Capabilities.SCRIPTING_CSV_MULTIROW_COLUMN_TEXT])
|
|
162
|
+
}
|
|
163
|
+
}
|
|
154
164
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
const conversationIds = _.uniq(rows.map(r => r[colConversationId]))
|
|
166
|
+
const convos = conversationIds.map(conversationId => {
|
|
167
|
+
const convoRows = rows.map((row, i) => {
|
|
168
|
+
if (row[colConversationId] === conversationId) {
|
|
169
|
+
return Object.assign({},
|
|
170
|
+
linesToConvoStep(
|
|
171
|
+
[row[colText]],
|
|
172
|
+
row[colSender],
|
|
173
|
+
this.context,
|
|
174
|
+
undefined,
|
|
175
|
+
true
|
|
176
|
+
), { stepTag: `L${i + lineNumberBase}` })
|
|
177
|
+
}
|
|
178
|
+
return null
|
|
179
|
+
}).filter(c => c)
|
|
180
|
+
return new Convo(this.context, {
|
|
181
|
+
header: {
|
|
182
|
+
name: conversationId
|
|
183
|
+
},
|
|
184
|
+
conversation: convoRows
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
|
|
188
|
+
this.context.AddConvos(convos)
|
|
189
|
+
} else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
|
|
190
|
+
this.context.AddPartialConvos(convos)
|
|
191
|
+
}
|
|
192
|
+
debug(`Found 3-column CSV file, treating it as multi-row conversation file, extracted ${convos.length} convos`)
|
|
193
|
+
return convos
|
|
194
|
+
}
|
|
195
|
+
} else if (scriptType === Constants.SCRIPTING_TYPE_UTTERANCES) {
|
|
196
|
+
if (columnCount === 2 || columnCount === 3 || (legacyModeOn && columnCount > 4)) {
|
|
197
|
+
debug(`Invalid column count '${columnCount}' in utterances mode`)
|
|
198
|
+
return []
|
|
199
|
+
}
|
|
200
|
+
const result = []
|
|
201
|
+
const startRow = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW, 2) - 1
|
|
202
|
+
const startRowHeader = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STARTROW_HEADER)
|
|
203
|
+
const stopOnEmpty = this._GetOptionalCapability(Capabilities.SCRIPTING_CSV_UTTERANCE_STOP_ON_EMPTY)
|
|
204
|
+
|
|
205
|
+
for (let col = 0; col < columnCount; col++) {
|
|
206
|
+
const name = rows[0][col]
|
|
207
|
+
if (!name || name.trim().length === 0) {
|
|
208
|
+
debug(`Column ${col + 1} has no header, skipping`)
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
158
211
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
212
|
+
const uttStruct = {
|
|
213
|
+
name,
|
|
214
|
+
utterances: []
|
|
215
|
+
}
|
|
216
|
+
let skip = !!startRowHeader
|
|
217
|
+
const getData = (row) => {
|
|
218
|
+
return rows[row][col] ? rows[row][col].trim() : false
|
|
162
219
|
}
|
|
163
|
-
|
|
164
|
-
|
|
220
|
+
//
|
|
221
|
+
for (let row = startRow; row < rows.length && (skip || !stopOnEmpty || !!getData(row)); row++) { // eslint-disable-line no-unmodified-loop-condition
|
|
222
|
+
const data = getData(row)
|
|
223
|
+
if (!data) {
|
|
224
|
+
continue
|
|
225
|
+
}
|
|
226
|
+
if (!skip) {
|
|
227
|
+
uttStruct.utterances.push(data)
|
|
228
|
+
} else {
|
|
229
|
+
if (startRowHeader === rows[row][col]) {
|
|
230
|
+
skip = false
|
|
231
|
+
}
|
|
232
|
+
}
|
|
165
233
|
}
|
|
166
|
-
if (
|
|
167
|
-
|
|
234
|
+
if (uttStruct.utterances.length === 0) {
|
|
235
|
+
// liveperson, skipping meta intents
|
|
236
|
+
debug(`Column ${col + 1} has no utterances, skipping`)
|
|
237
|
+
continue
|
|
168
238
|
}
|
|
239
|
+
result.push(uttStruct)
|
|
169
240
|
}
|
|
170
241
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
linesToConvoStep(
|
|
177
|
-
[row[colText]],
|
|
178
|
-
row[colSender],
|
|
179
|
-
this.context,
|
|
180
|
-
undefined,
|
|
181
|
-
true
|
|
182
|
-
), { stepTag: `L${i + lineNumberBase}` })
|
|
183
|
-
}
|
|
184
|
-
return null
|
|
185
|
-
}).filter(c => c)
|
|
186
|
-
return new Convo(this.context, {
|
|
187
|
-
header: {
|
|
188
|
-
name: conversationId
|
|
189
|
-
},
|
|
190
|
-
conversation: convoRows
|
|
191
|
-
})
|
|
192
|
-
})
|
|
193
|
-
if (scriptType === Constants.SCRIPTING_TYPE_CONVO) {
|
|
194
|
-
this.context.AddConvos(convos)
|
|
195
|
-
} else if (scriptType === Constants.SCRIPTING_TYPE_PCONVO) {
|
|
196
|
-
this.context.AddPartialConvos(convos)
|
|
197
|
-
}
|
|
198
|
-
return convos
|
|
242
|
+
debug(`Multi-column utterance file, extracted ${result.length} utterances`)
|
|
243
|
+
this.context.AddUtterances(result)
|
|
244
|
+
return result
|
|
245
|
+
} else {
|
|
246
|
+
return []
|
|
199
247
|
}
|
|
200
248
|
}
|
|
201
249
|
}
|
package/src/scripting/Convo.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const _ = require('lodash')
|
|
3
3
|
const debug = require('debug')('botium-core-Convo')
|
|
4
|
+
const promiseRetry = require('promise-retry')
|
|
4
5
|
|
|
5
6
|
const BotiumMockMessage = require('../mocks/BotiumMockMessage')
|
|
6
7
|
const Capabilities = require('../Capabilities')
|
|
@@ -8,6 +9,7 @@ const Events = require('../Events')
|
|
|
8
9
|
const ScriptingMemory = require('./ScriptingMemory')
|
|
9
10
|
const { BotiumError, botiumErrorFromErr, botiumErrorFromList } = require('./BotiumError')
|
|
10
11
|
const { normalizeText, toString, removeBuffers, splitStringInNonEmptyLines } = require('./helper')
|
|
12
|
+
const RetryHelper = require('../helpers/RetryHelper')
|
|
11
13
|
|
|
12
14
|
const { LOGIC_HOOK_INCLUDE } = require('./logichook/LogicHookConsts')
|
|
13
15
|
|
|
@@ -210,6 +212,24 @@ class Convo {
|
|
|
210
212
|
}
|
|
211
213
|
|
|
212
214
|
async Run (container) {
|
|
215
|
+
const retryHelper = new RetryHelper(container.caps, 'CONVO')
|
|
216
|
+
return promiseRetry(async (retry, number) => {
|
|
217
|
+
return this.RunImpl(container).catch(err => {
|
|
218
|
+
const retryRemaining = retryHelper.retrySettings.retries - number + 1
|
|
219
|
+
if (retryHelper.shouldRetry(err)) {
|
|
220
|
+
debug(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry ${retryRemaining > 0 ? 'enabled' : 'disabled'} (remaining #${retryRemaining}/${retryHelper.retrySettings.retries}, criterion matches)`)
|
|
221
|
+
retry(err)
|
|
222
|
+
} else {
|
|
223
|
+
if (retryHelper.retryErrorPatterns.length > 0) {
|
|
224
|
+
debug(`Convo failed with error "${err.message || JSON.stringify(err)}". Retry 'disabled' (remaining (#${retryRemaining}/${retryHelper.retrySettings.retries}), criterion does not match)`)
|
|
225
|
+
}
|
|
226
|
+
throw err
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
}, retryHelper.retrySettings)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async RunImpl (container) {
|
|
213
233
|
const transcript = new Transcript({
|
|
214
234
|
steps: [],
|
|
215
235
|
attachments: [],
|
|
@@ -20,7 +20,7 @@ const RetryHelper = require('../helpers/RetryHelper')
|
|
|
20
20
|
const { getMatchFunction } = require('./MatchFunctions')
|
|
21
21
|
const precompilers = require('./precompilers')
|
|
22
22
|
|
|
23
|
-
const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.yaml|*.yml|*.json|*.md|*.markdown)'
|
|
23
|
+
const globPattern = '**/+(*.convo.txt|*.utterances.txt|*.pconvo.txt|*.scriptingmemory.txt|*.xlsx|*.xlsm|*.convo.csv|*.pconvo.csv|*.utterances.csv|*.yaml|*.yml|*.json|*.md|*.markdown)'
|
|
24
24
|
const skipPattern = /^skip[.\-_]/i
|
|
25
25
|
|
|
26
26
|
const p = (retryHelper, fn) => {
|
|
@@ -522,6 +522,10 @@ module.exports = class ScriptingProvider {
|
|
|
522
522
|
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_CONVO)
|
|
523
523
|
} else if (filename.endsWith('.pconvo.csv')) {
|
|
524
524
|
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO)
|
|
525
|
+
} else if (filename.endsWith('.pconvo.csv')) {
|
|
526
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_PCONVO)
|
|
527
|
+
} else if (filename.endsWith('.utterance.csv')) {
|
|
528
|
+
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_CSV, Constants.SCRIPTING_TYPE_UTTERANCES)
|
|
525
529
|
} else if (filename.endsWith('.yaml') || filename.endsWith('.yml')) {
|
|
526
530
|
result = this.ReadScriptFromBuffer(scriptBuffer, Constants.SCRIPTING_FORMAT_YAML, [Constants.SCRIPTING_TYPE_UTTERANCES, Constants.SCRIPTING_TYPE_PCONVO, Constants.SCRIPTING_TYPE_CONVO, Constants.SCRIPTING_TYPE_SCRIPTING_MEMORY])
|
|
527
531
|
} else if (filename.endsWith('.json')) {
|
|
@@ -192,7 +192,7 @@ module.exports = class LogicHookUtils {
|
|
|
192
192
|
})
|
|
193
193
|
return vm.run(script)
|
|
194
194
|
} catch (err) {
|
|
195
|
-
throw new Error(
|
|
195
|
+
throw new Error(`Script ${key} is not valid - ${err.message || err}`)
|
|
196
196
|
}
|
|
197
197
|
} else {
|
|
198
198
|
throw new Error(`Script "${key}" is not valid - only functions and javascript code accepted`)
|