botium-core 1.12.4 → 1.13.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.
- package/LICENSES-3RDPARTY.txt +390 -225
- package/dist/botium-cjs.js +298 -161
- package/dist/botium-cjs.js.map +1 -1
- package/dist/botium-es.js +298 -161
- package/dist/botium-es.js.map +1 -1
- package/package.json +28 -28
- package/src/BotDriver.js +24 -19
- package/src/Capabilities.js +8 -0
- package/src/Defaults.js +1 -0
- package/src/containers/plugins/SimpleRestContainer.js +42 -8
- package/src/containers/plugins/index.js +1 -1
- package/src/helpers/Utils.js +1 -1
- package/src/scripting/CompilerCsv.js +150 -102
- package/src/scripting/CompilerXlsx.js +2 -2
- package/src/scripting/Convo.js +4 -4
- package/src/scripting/ScriptingProvider.js +6 -2
- package/src/scripting/helper.js +20 -13
- package/src/scripting/logichook/asserter/BaseCountAsserter.js +1 -1
- package/src/scripting/logichook/asserter/ButtonsAsserter.js +4 -2
- package/src/scripting/logichook/asserter/CardsAsserter.js +4 -2
- package/test/compiler/compilercsv.spec.js +363 -12
- package/test/compiler/compilertxt.spec.js +13 -0
- 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/compiler/convos/txt/convos_args_escape.convo.txt +2 -0
- package/test/connectors/simplerest.spec.js +49 -8
- package/test/convo/convos/continuefailing_timeout.convo.txt +16 -0
- package/test/convo/transcript.spec.js +18 -1
- package/test/logichooks/convos/WAITFORBOT_INFINITE.convo.txt +1 -1
- package/test/scripting/asserters/buttonsAsserter.spec.js +47 -0
- package/test/scripting/asserters/cardsAsserter.spec.js +39 -0
- package/test/scripting/scriptingProvider.spec.js +1 -1
- package/test/scripting/txt/decompile.spec.js +24 -0
|
@@ -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
|
}
|
|
@@ -339,7 +339,7 @@ module.exports = class CompilerXlsx extends CompilerBase {
|
|
|
339
339
|
agg[varName] = varValues[varIndex][caseIndex] || null
|
|
340
340
|
return agg
|
|
341
341
|
}, {})
|
|
342
|
-
scriptResults.push({ header: { name: caseName }, values
|
|
342
|
+
scriptResults.push({ header: { name: caseName }, values })
|
|
343
343
|
}
|
|
344
344
|
} else {
|
|
345
345
|
const variableNames = []
|
|
@@ -370,7 +370,7 @@ module.exports = class CompilerXlsx extends CompilerBase {
|
|
|
370
370
|
}
|
|
371
371
|
rowindex += 1
|
|
372
372
|
|
|
373
|
-
scriptResults.push({ header: { name: caseName }, values
|
|
373
|
+
scriptResults.push({ header: { name: caseName }, values })
|
|
374
374
|
} else {
|
|
375
375
|
break
|
|
376
376
|
}
|
package/src/scripting/Convo.js
CHANGED
|
@@ -235,14 +235,14 @@ class Convo {
|
|
|
235
235
|
await this.runConversation(container, scriptingMemory, transcript)
|
|
236
236
|
await this._checkBotRepliesConsumed(container)
|
|
237
237
|
try {
|
|
238
|
-
await this.scriptingEvents.onConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory
|
|
238
|
+
await this.scriptingEvents.onConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory })
|
|
239
239
|
} catch (err) {
|
|
240
240
|
throw new TranscriptError(botiumErrorFromErr(`${this.header.name}: ${err.message}`, err), transcript)
|
|
241
241
|
}
|
|
242
242
|
if (transcript.err && container.caps[Capabilities.SCRIPTING_ENABLE_MULTIPLE_ASSERT_ERRORS]) {
|
|
243
243
|
let assertConvoEndErr = null
|
|
244
244
|
try {
|
|
245
|
-
await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory
|
|
245
|
+
await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory })
|
|
246
246
|
} catch (err) {
|
|
247
247
|
assertConvoEndErr = botiumErrorFromErr(`${this.header.name}: ${err.message}`, err)
|
|
248
248
|
}
|
|
@@ -257,7 +257,7 @@ class Convo {
|
|
|
257
257
|
throw new TranscriptError(transcript.err, transcript)
|
|
258
258
|
}
|
|
259
259
|
try {
|
|
260
|
-
await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory
|
|
260
|
+
await this.scriptingEvents.assertConvoEnd({ convo: this, convoStep: { stepTag: '#end' }, container, transcript, scriptingMemory })
|
|
261
261
|
} catch (err) {
|
|
262
262
|
transcript.err = botiumErrorFromErr(`${this.header.name}: ${err.message}`, err)
|
|
263
263
|
throw new TranscriptError(transcript.err, transcript)
|
|
@@ -535,7 +535,7 @@ class Convo {
|
|
|
535
535
|
if (container.caps[Capabilities.SCRIPTING_ENABLE_SKIP_ASSERT_ERRORS]) {
|
|
536
536
|
const transcriptStepErrs = transcript.steps.filter(s => s.err).map(s => s.err)
|
|
537
537
|
if (transcriptStepErrs && transcriptStepErrs.length > 0) {
|
|
538
|
-
transcript.err = botiumErrorFromList([transcriptStepErrs, transcript.err].filter(e => e), {})
|
|
538
|
+
transcript.err = botiumErrorFromList([...transcriptStepErrs.filter(err => err !== transcript.err), transcript.err].filter(e => e), {})
|
|
539
539
|
}
|
|
540
540
|
}
|
|
541
541
|
}
|
|
@@ -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')) {
|
|
@@ -1192,7 +1196,7 @@ module.exports = class ScriptingProvider {
|
|
|
1192
1196
|
const node = {
|
|
1193
1197
|
sender: convoNode.sender,
|
|
1194
1198
|
key: randomize('0', 20),
|
|
1195
|
-
hash
|
|
1199
|
+
hash,
|
|
1196
1200
|
convoNodes: convoNodeValues,
|
|
1197
1201
|
convos: [_.cloneDeep(convoNodeHeader)],
|
|
1198
1202
|
childNodes: []
|
package/src/scripting/helper.js
CHANGED
|
@@ -77,6 +77,13 @@ const flatString = (str) => {
|
|
|
77
77
|
return str ? str.split('\n').map(s => s.trim()).join(' ') : ''
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
const _formatAppendArgs = (args) => {
|
|
81
|
+
return args ? ` ${args.map(a => _.isString(a) ? a.replace(/\|/g, '\\|') : `${a}`).join('|')}` : ''
|
|
82
|
+
}
|
|
83
|
+
const _parseArgs = (str) => {
|
|
84
|
+
return (str && str.length > 0 && str.replace(/\\\|/g, '###ESCAPESPLIT###').split('|').map(s => s.replace(/###ESCAPESPLIT###/g, '|').trim())) || []
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
const linesToConvoStep = (lines, sender, context, eol, singleLineMode = false) => {
|
|
81
88
|
if (!validateSender(sender)) throw new Error(`Failed to parse conversation. Section "${sender}" unknown.`)
|
|
82
89
|
|
|
@@ -106,14 +113,14 @@ const linesToConvoStep = (lines, sender, context, eol, singleLineMode = false) =
|
|
|
106
113
|
}
|
|
107
114
|
const name = logicLine.split(' ')[0]
|
|
108
115
|
if (sender !== 'me' && context.IsAsserterValid(name)) {
|
|
109
|
-
const args = (logicLine.length > name.length ? logicLine.substr(name.length + 1)
|
|
116
|
+
const args = (logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [])
|
|
110
117
|
convoStep.asserters.push({ name, args, not, optional })
|
|
111
118
|
} else if (sender === 'me' && context.IsUserInputValid(name)) {
|
|
112
|
-
const args = (logicLine.length > name.length ? logicLine.substr(name.length + 1)
|
|
119
|
+
const args = (logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [])
|
|
113
120
|
convoStep.userInputs.push({ name, args })
|
|
114
121
|
textLinesAccepted = false
|
|
115
122
|
} else if (context.IsLogicHookValid(name)) {
|
|
116
|
-
const args = (logicLine.length > name.length ? logicLine.substr(name.length + 1)
|
|
123
|
+
const args = (logicLine.length > name.length ? _parseArgs(logicLine.substr(name.length + 1)) : [])
|
|
117
124
|
convoStep.logicHooks.push({ name, args })
|
|
118
125
|
textLinesAccepted = false
|
|
119
126
|
} else {
|
|
@@ -422,7 +429,7 @@ const convoStepToLines = (step) => {
|
|
|
422
429
|
const lines = []
|
|
423
430
|
if (step.sender === 'me') {
|
|
424
431
|
step.forms && step.forms.filter(form => form.value).forEach((form) => {
|
|
425
|
-
lines.push(`FORM
|
|
432
|
+
lines.push(`FORM${_formatAppendArgs([form.name, form.value])}`)
|
|
426
433
|
})
|
|
427
434
|
if (step.buttons && step.buttons.length > 0) {
|
|
428
435
|
lines.push('BUTTON ' + _decompileButton(step.buttons[0]))
|
|
@@ -432,34 +439,34 @@ const convoStepToLines = (step) => {
|
|
|
432
439
|
lines.push(step.messageText)
|
|
433
440
|
}
|
|
434
441
|
step.userInputs && step.userInputs.forEach((userInput) => {
|
|
435
|
-
lines.push(userInput.name + (userInput.args
|
|
442
|
+
lines.push(userInput.name + _formatAppendArgs(userInput.args))
|
|
436
443
|
})
|
|
437
444
|
step.logicHooks && step.logicHooks.forEach((logicHook) => {
|
|
438
|
-
lines.push(logicHook.name + (logicHook.args
|
|
445
|
+
lines.push(logicHook.name + _formatAppendArgs(logicHook.args))
|
|
439
446
|
})
|
|
440
447
|
} else {
|
|
441
448
|
if (step.messageText) {
|
|
442
449
|
lines.push((step.optional ? '?' : '') + (step.not ? '!' : '') + step.messageText)
|
|
443
450
|
}
|
|
444
|
-
if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS
|
|
445
|
-
if (step.media && step.media.length > 0) lines.push('MEDIA
|
|
451
|
+
if (step.buttons && step.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(step.buttons.filter(b => b.text).map(b => flatString(b.text))))
|
|
452
|
+
if (step.media && step.media.length > 0) lines.push('MEDIA' + _formatAppendArgs(step.media.filter(m => !m.buffer && m.mediaUri).map(m => m.mediaUri)))
|
|
446
453
|
if (step.cards && step.cards.length > 0) {
|
|
447
454
|
step.cards.forEach(c => {
|
|
448
455
|
let cardTexts = []
|
|
449
456
|
if (c.text) cardTexts = cardTexts.concat(_.isArray(c.text) ? c.text : [c.text])
|
|
450
457
|
if (c.subtext) cardTexts = cardTexts.concat(_.isArray(c.subtext) ? c.subtext : [c.subtext])
|
|
451
458
|
if (c.content) cardTexts = cardTexts.concat(_.isArray(c.content) ? c.content : [c.content])
|
|
452
|
-
if (cardTexts.length > 0) lines.push('CARDS
|
|
459
|
+
if (cardTexts.length > 0) lines.push('CARDS' + _formatAppendArgs(cardTexts.map(c => flatString(c))))
|
|
453
460
|
|
|
454
|
-
if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS
|
|
461
|
+
if (c.buttons && c.buttons.length > 0) lines.push('BUTTONS' + _formatAppendArgs(c.buttons.filter(b => b.text).map(b => flatString(b.text))))
|
|
455
462
|
if (c.image && !c.image.buffer && c.image.mediaUri) lines.push('MEDIA ' + c.image.mediaUri)
|
|
456
463
|
})
|
|
457
464
|
}
|
|
458
465
|
step.asserters && step.asserters.forEach((asserter) => {
|
|
459
|
-
lines.push((asserter.optional ? '?' : '') + (asserter.not ? '!' : '') + asserter.name + (asserter.args
|
|
466
|
+
lines.push((asserter.optional ? '?' : '') + (asserter.not ? '!' : '') + asserter.name + _formatAppendArgs(asserter.args))
|
|
460
467
|
})
|
|
461
468
|
step.logicHooks && step.logicHooks.forEach((logicHook) => {
|
|
462
|
-
lines.push(logicHook.name + (logicHook.args
|
|
469
|
+
lines.push(logicHook.name + _formatAppendArgs(logicHook.args))
|
|
463
470
|
})
|
|
464
471
|
}
|
|
465
472
|
return lines.map(l => l.trim())
|
|
@@ -494,7 +501,7 @@ const linesToScriptingMemories = (lines, columnMode = null) => {
|
|
|
494
501
|
agg[varName] = varValues[varIndex][caseIndex] || null
|
|
495
502
|
return agg
|
|
496
503
|
}, {})
|
|
497
|
-
const scriptingMemory = { header: { name: caseName }, values
|
|
504
|
+
const scriptingMemory = { header: { name: caseName }, values }
|
|
498
505
|
scriptingMemories.push(scriptingMemory)
|
|
499
506
|
}
|
|
500
507
|
} else {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
const { SCRIPTING_NORMALIZE_TEXT } = require('../../../Capabilities')
|
|
1
2
|
const { BotiumError } = require('../../BotiumError')
|
|
2
3
|
const { buttonsFromMsg } = require('../helpers')
|
|
4
|
+
const { normalizeText } = require('../../helper')
|
|
3
5
|
|
|
4
6
|
module.exports = class ButtonsAsserter {
|
|
5
7
|
constructor (context, caps = {}) {
|
|
@@ -9,14 +11,14 @@ module.exports = class ButtonsAsserter {
|
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
_evalButtons (args, botMsg) {
|
|
12
|
-
const allButtons = buttonsFromMsg(botMsg, true).map(b => b.text)
|
|
14
|
+
const allButtons = buttonsFromMsg(botMsg, true).map(b => b.text).filter(b => b).map(b => normalizeText(b, !!this.caps[SCRIPTING_NORMALIZE_TEXT]))
|
|
13
15
|
if (!args || args.length === 0) {
|
|
14
16
|
return { allButtons, buttonsNotFound: [], buttonsFound: allButtons }
|
|
15
17
|
}
|
|
16
18
|
const buttonsNotFound = []
|
|
17
19
|
const buttonsFound = []
|
|
18
20
|
for (let i = 0; i < (args || []).length; i++) {
|
|
19
|
-
if (allButtons.findIndex(b => this.context.Match(b, args[i])) < 0) {
|
|
21
|
+
if (allButtons.findIndex(b => this.context.Match(b, normalizeText(args[i], !!this.caps[SCRIPTING_NORMALIZE_TEXT]))) < 0) {
|
|
20
22
|
buttonsNotFound.push(args[i])
|
|
21
23
|
} else {
|
|
22
24
|
buttonsFound.push(args[i])
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
const { SCRIPTING_NORMALIZE_TEXT } = require('../../../Capabilities')
|
|
1
2
|
const { BotiumError } = require('../../BotiumError')
|
|
2
3
|
const { cardsFromMsg } = require('../helpers')
|
|
4
|
+
const { normalizeText } = require('../../helper')
|
|
3
5
|
|
|
4
6
|
module.exports = class CardsAsserter {
|
|
5
7
|
constructor (context, caps = {}) {
|
|
@@ -9,14 +11,14 @@ module.exports = class CardsAsserter {
|
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
_evalCards (args, botMsg) {
|
|
12
|
-
const allCards = cardsFromMsg(botMsg, true).reduce((acc, mc) => acc.concat([mc.text, mc.subtext, mc.content].filter(t => t)), [])
|
|
14
|
+
const allCards = cardsFromMsg(botMsg, true).reduce((acc, mc) => acc.concat([mc.text, mc.subtext, mc.content].filter(t => t).map(t => normalizeText(t, !!this.caps[SCRIPTING_NORMALIZE_TEXT]))), [])
|
|
13
15
|
if (!args || args.length === 0) {
|
|
14
16
|
return { allCards, cardsNotFound: [], cardsFound: allCards }
|
|
15
17
|
}
|
|
16
18
|
const cardsNotFound = []
|
|
17
19
|
const cardsFound = []
|
|
18
20
|
for (let i = 0; i < (args || []).length; i++) {
|
|
19
|
-
if (allCards.findIndex(c => this.context.Match(c, args[i])) < 0) {
|
|
21
|
+
if (allCards.findIndex(c => this.context.Match(c, normalizeText(args[i], !!this.caps[SCRIPTING_NORMALIZE_TEXT]))) < 0) {
|
|
20
22
|
cardsNotFound.push(args[i])
|
|
21
23
|
} else {
|
|
22
24
|
cardsFound.push(args[i])
|