botium-core 1.15.8 → 1.15.10

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.
@@ -77,7 +77,7 @@ var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
77
77
  var bodyParser__default = /*#__PURE__*/_interopDefaultLegacy(bodyParser);
78
78
 
79
79
  var name = "botium-core";
80
- var version$1 = "1.15.8";
80
+ var version$1 = "1.15.10";
81
81
  var description = "The Selenium for Chatbots";
82
82
  var main = "index.js";
83
83
  var module$1 = "dist/botium-es.js";
@@ -3039,6 +3039,7 @@ class Convo$6 {
3039
3039
  }
3040
3040
  async runConversation(container, scriptingMemory, transcript) {
3041
3041
  const transcriptSteps = [];
3042
+ transcript.steps = transcriptSteps;
3042
3043
  try {
3043
3044
  let lastMeConvoStep = null;
3044
3045
  let botMsg = null;
@@ -3223,6 +3224,21 @@ class Convo$6 {
3223
3224
  throw failErr;
3224
3225
  }
3225
3226
  } else if (convoStep.sender === 'bot') {
3227
+ if (this.scriptingEvents.executeBotStep) {
3228
+ const executeBotStepResult = await this.scriptingEvents.executeBotStep({
3229
+ convo: this,
3230
+ convoStep,
3231
+ container,
3232
+ scriptingMemory,
3233
+ transcript,
3234
+ transcriptStep,
3235
+ transcriptSteps
3236
+ });
3237
+ if (executeBotStepResult) {
3238
+ skipTranscriptStep = true;
3239
+ continue;
3240
+ }
3241
+ }
3226
3242
  if (waitForBotSays) {
3227
3243
  botMsg = null;
3228
3244
  } else {
@@ -5796,6 +5812,31 @@ var ScriptingProvider_1 = class ScriptingProvider {
5796
5812
  ...rest
5797
5813
  });
5798
5814
  },
5815
+ executeBotStep: ({
5816
+ convo,
5817
+ convoStep,
5818
+ container,
5819
+ scriptingMemory,
5820
+ ...rest
5821
+ }) => {
5822
+ const logicHooks = convoStep?.logicHooks || [];
5823
+ const executeBotStepHooks = logicHooks.filter(l => this.logicHooks[l.name] && typeof this.logicHooks[l.name].executeBotStep === 'function');
5824
+ if (executeBotStepHooks.length > 1) {
5825
+ throw new Error(`${convo?.header?.name}/${convoStep?.stepTag}: Multiple logic hooks implement executeBotStep: ${executeBotStepHooks.map(l => l.name).join(', ')}. Only one is allowed per step.`);
5826
+ }
5827
+ if (executeBotStepHooks.length === 1) {
5828
+ const lh = executeBotStepHooks[0];
5829
+ return this.logicHooks[lh.name].executeBotStep({
5830
+ convo,
5831
+ convoStep,
5832
+ scriptingMemory,
5833
+ container,
5834
+ args: ScriptingMemory.applyToArgs(lh.args, scriptingMemory, container.caps),
5835
+ ...rest
5836
+ });
5837
+ }
5838
+ return null;
5839
+ },
5799
5840
  assertConvoBegin: ({
5800
5841
  convo,
5801
5842
  convoStep,
@@ -6097,12 +6138,18 @@ var ScriptingProvider_1 = class ScriptingProvider {
6097
6138
  if (hookType !== 'onMeStart' && hookType !== 'onMePrepare' && hookType !== 'onMeEnd' && hookType !== 'onBotStart' && hookType !== 'onBotPrepare' && hookType !== 'onBotEnd' && hookType !== 'onConvoBegin' && hookType !== 'onConvoEnd') {
6098
6139
  throw Error(`Unknown hookType ${hookType}`);
6099
6140
  }
6100
- const localHooks = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType]);
6141
+ let localHooks = (logicHooks || []).filter(l => this.logicHooks[l.name][hookType]);
6142
+ // Scripting memory file are injected via SET_SCRIPTING_MEMORY in the BEGIN step
6143
+ // But there might be other logic hooks that need the scripting memory variables
6144
+ // Order is important (SET_SCRIPTING_MEMORY in begin can be because the user added it,
6145
+ // or because the scripting memory file added it. User one has to be the last one.
6146
+ localHooks = [...localHooks.filter(l => l.name === 'SET_SCRIPTING_MEMORY'), ...localHooks.filter(l => l.name !== 'SET_SCRIPTING_MEMORY')];
6101
6147
  const convoStepPromises = localHooks.map(l => p(this.retryHelperLogicHook, () => this.logicHooks[l.name][hookType]({
6102
6148
  convo,
6103
6149
  convoStep,
6104
6150
  scriptingMemory,
6105
6151
  container,
6152
+ // Do this more sensitve for SET_SCRIPTING_MEMORY? It can have scripting variables in the args
6106
6153
  args: ScriptingMemory.applyToArgs(l.args, scriptingMemory, container.caps, rest.botMsg),
6107
6154
  isGlobal: false,
6108
6155
  ...rest
@@ -6192,6 +6239,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6192
6239
  onBotStart: this.scriptingEvents.onBotStart.bind(this),
6193
6240
  onBotPrepare: this.scriptingEvents.onBotPrepare.bind(this),
6194
6241
  onBotEnd: this.scriptingEvents.onBotEnd.bind(this),
6242
+ executeBotStep: this.scriptingEvents.executeBotStep.bind(this),
6195
6243
  setUserInput: this.scriptingEvents.setUserInput.bind(this),
6196
6244
  fail: this.scriptingEvents.fail && this.scriptingEvents.fail.bind(this)
6197
6245
  }
@@ -6712,7 +6760,11 @@ var ScriptingProvider_1 = class ScriptingProvider {
6712
6760
  // use skip and keep, or justHeader
6713
6761
  justHeader: false,
6714
6762
  // drop unwanted convos
6715
- convoFilter: null
6763
+ convoFilter: null,
6764
+ mediaInput: {
6765
+ // MESSAGE_TEXT_FROM_FILENAME or MESSAGE_TEXT_FROM_TRANSCRIPTION or falsy
6766
+ messageTextMode: null
6767
+ }
6716
6768
  }, options);
6717
6769
  const expandedConvos = [];
6718
6770
  // The globalContext is going to keep the data even if the Object.assign which happening to create the myContext in _expandConvo function
@@ -6750,7 +6802,11 @@ var ScriptingProvider_1 = class ScriptingProvider {
6750
6802
  ExpandConvosIterable(options = {}) {
6751
6803
  options = Object.assign({
6752
6804
  // drop unwanted convos
6753
- convoFilter: null
6805
+ convoFilter: null,
6806
+ mediaInput: {
6807
+ // MESSAGE_TEXT_FROM_FILENAME or MESSAGE_TEXT_FROM_TRANSCRIPTION or falsy
6808
+ messageTextMode: null
6809
+ }
6754
6810
  }, options);
6755
6811
  // The globalContext is going to keep the data even if the Object.assign which happening to create the myContext in _expandConvo function
6756
6812
  const context = {
@@ -6897,7 +6953,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
6897
6953
  const expandedUserInputs = userInput.expandConvo({
6898
6954
  convo: currentConvo,
6899
6955
  convoStep: currentStep,
6900
- args: ui.args
6956
+ args: ui.args,
6957
+ options
6901
6958
  });
6902
6959
  if (expandedUserInputs && expandedUserInputs.length > 0) {
6903
6960
  // let sampleinputs = expandedUserInputs
@@ -6909,9 +6966,16 @@ var ScriptingProvider_1 = class ScriptingProvider {
6909
6966
  }
6910
6967
  };
6911
6968
  const processSampleInput = function* (sampleinput, length, index, myContext, uiIndex) {
6969
+ const {
6970
+ messageText,
6971
+ ...userInput
6972
+ } = sampleinput;
6912
6973
  const currentStepsStack = convoStepsStack.slice();
6913
6974
  const currentStepMod = lodash__default["default"].cloneDeep(currentStep);
6914
- currentStepMod.userInputs[uiIndex] = sampleinput;
6975
+ currentStepMod.userInputs[uiIndex] = userInput;
6976
+ if (messageText) {
6977
+ currentStepMod.messageText = messageText;
6978
+ }
6915
6979
  currentStepsStack.push(currentStepMod);
6916
6980
  const currentConvoLabeled = lodash__default["default"].cloneDeep(currentConvo);
6917
6981
  if (length > 1) {
@@ -9571,8 +9635,8 @@ var BotDriver_1 = class BotDriver {
9571
9635
  if (lodash__default["default"].isString(newCaps[capKey])) {
9572
9636
  try {
9573
9637
  caps[capKey] = JSON.parse(newCaps[capKey]);
9574
- if (lodash__default["default"].isFinite(caps[capKey])) {
9575
- caps[capKey] = caps[capKey].toString();
9638
+ if (lodash__default["default"].isFinite(Number(newCaps[capKey]))) {
9639
+ caps[capKey] = newCaps[capKey].toString();
9576
9640
  }
9577
9641
  } catch (err) {
9578
9642
  caps[capKey] = newCaps[capKey];
@@ -9831,6 +9895,79 @@ Plugins.PLUGIN_TYPE_LOGICHOOK;
9831
9895
  Plugins.PLUGIN_TYPE_USERINPUT;
9832
9896
  Plugins.TYPE_TO_PREFIX;
9833
9897
 
9898
+ const {
9899
+ parse: csvParseSync
9900
+ } = sync__default["default"];
9901
+
9902
+ /**
9903
+ * Find transcription for an audio file: .txt same base name, or transcript.csv in parent dirs.
9904
+ * @param {string} baseDir - Base directory for resolving paths
9905
+ * @param {string} audioFile - Relative path to audio file
9906
+ * @param {object} [options] - Optional: { csvCache: {}, onError: (msg) => {} }
9907
+ * @returns {string|null} Transcription text or null
9908
+ */
9909
+ var findTranscription = (baseDir, audioFile, options = {}) => {
9910
+ const {
9911
+ csvCache = {},
9912
+ onError
9913
+ } = options;
9914
+ const transcriptionFilename = `${audioFile.substring(0, audioFile.lastIndexOf('.'))}.txt`;
9915
+ const transcriptionFilenameAbs = path__default["default"].resolve(baseDir, transcriptionFilename);
9916
+ try {
9917
+ if (fs__default["default"].existsSync(transcriptionFilenameAbs)) {
9918
+ return fs__default["default"].readFileSync(transcriptionFilenameAbs, {
9919
+ encoding: 'utf-8'
9920
+ }).trim();
9921
+ }
9922
+ } catch (err) {
9923
+ if (onError) onError(`Transcription File ${transcriptionFilenameAbs} not readable: ${err.message}`);
9924
+ throw new Error(`Reading transcription file ${transcriptionFilename} for ${audioFile} failed`);
9925
+ }
9926
+ if (csvCache[audioFile]) {
9927
+ return csvCache[audioFile];
9928
+ }
9929
+ const audioFileComponents = audioFile.split('/');
9930
+ for (let parentIndex = audioFileComponents.length - 1; parentIndex >= 0; parentIndex--) {
9931
+ const csvDirectory = audioFileComponents.slice(0, parentIndex);
9932
+ const csvFilename = path__default["default"].join(...csvDirectory, 'transcript.csv');
9933
+ const csvFilenameAbs = path__default["default"].resolve(baseDir, csvFilename);
9934
+ try {
9935
+ if (fs__default["default"].existsSync(csvFilenameAbs)) {
9936
+ const records = csvParseSync(fs__default["default"].readFileSync(csvFilenameAbs, {
9937
+ encoding: 'utf-8'
9938
+ }).trim(), {
9939
+ columns: ['filename', 'transcription'],
9940
+ delimiter: [',', ';', ':', '\t'],
9941
+ trim: true,
9942
+ skip_empty_lines: true
9943
+ });
9944
+ if (records && records.length > 0) {
9945
+ for (const record of records) {
9946
+ const fnKey = path__default["default"].join(...csvDirectory, record.filename);
9947
+ csvCache[fnKey] = record.transcription;
9948
+ }
9949
+ }
9950
+ }
9951
+ } catch (err) {
9952
+ if (onError) onError(`Transcription CSV File ${csvFilenameAbs} not readable: ${err.message}`);
9953
+ throw new Error(`Reading transcription CSV file for ${csvFilename} failed`);
9954
+ }
9955
+ if (csvCache[audioFile]) {
9956
+ return csvCache[audioFile];
9957
+ }
9958
+ }
9959
+ return null;
9960
+ };
9961
+
9962
+ /**
9963
+ * Derive transcription text from audio filename (basename without extension, underscores/hyphens → spaces).
9964
+ * @param {string} audioFile - Path or filename of audio file
9965
+ * @returns {string}
9966
+ */
9967
+ var transcriptionFromFilename = audioFile => {
9968
+ const filename = path__default["default"].basename(audioFile, path__default["default"].extname(audioFile));
9969
+ return filename.split(/[_-]+/).join(' ');
9970
+ };
9834
9971
  var hasWaitForBotTimeout = transciptError => {
9835
9972
  if (!transciptError) {
9836
9973
  return false;
@@ -9842,6 +9979,8 @@ var hasWaitForBotTimeout = transciptError => {
9842
9979
  return str.indexOf(': error waiting for bot - Bot did not respond within') > 0;
9843
9980
  };
9844
9981
  var TranscriptUtils = {
9982
+ findTranscription: findTranscription,
9983
+ transcriptionFromFilename: transcriptionFromFilename,
9845
9984
  hasWaitForBotTimeout: hasWaitForBotTimeout
9846
9985
  };
9847
9986