@onereach/step-voice 6.0.15 → 6.0.17

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/dst/Choice.d.ts CHANGED
@@ -13,6 +13,7 @@ interface INPUT {
13
13
  useInterspeechTimeout: boolean;
14
14
  interSpeechTimeout: number;
15
15
  recognitionModel?: string;
16
+ longRecognition: boolean;
16
17
  }
17
18
  interface EVENT extends VoiceEvent {
18
19
  callRecording?: TODO;
package/dst/Choice.js CHANGED
@@ -69,7 +69,7 @@ const isRepromptTrigger = (recogResult, promptsTriggers) => {
69
69
  class Choice extends voice_1.default {
70
70
  async runStep() {
71
71
  const call = await this.fetchData();
72
- const { textType, asr, tts, sensitiveData, noReplyDelay, usePromptsTriggers, recognitionModel, useInterspeechTimeout, interSpeechTimeout } = this.data;
72
+ const { textType, asr, tts, sensitiveData, noReplyDelay, usePromptsTriggers, recognitionModel, useInterspeechTimeout, interSpeechTimeout, longRecognition } = this.data;
73
73
  const exitExists = (exitId) => {
74
74
  return lodash_1.default.some(choices, (choice) => choice.exitId === exitId);
75
75
  };
@@ -87,7 +87,9 @@ class Choice extends voice_1.default {
87
87
  name: 'speak',
88
88
  params: {
89
89
  grammar,
90
- dictation: useInterspeechTimeout,
90
+ dictation: longRecognition
91
+ ? 'continuous'
92
+ : useInterspeechTimeout,
91
93
  interSpeechTimeout: interSpeechTimeout * 1000,
92
94
  sections: []
93
95
  }
@@ -111,7 +113,7 @@ class Choice extends voice_1.default {
111
113
  actionFromBot: false
112
114
  });
113
115
  await this.resumeRecording(call, sensitiveData);
114
- return this.exitStep(exitId, this.exitChoiceData('dtmf', params));
116
+ return this.exitStep(exitId, this.exitChoiceData('dtmf', params), longRecognition);
115
117
  }
116
118
  else if (this.rptsHasMore({ repromptsList })) {
117
119
  await this.transcript(call, {
@@ -139,9 +141,7 @@ class Choice extends voice_1.default {
139
141
  actionFromBot: false
140
142
  });
141
143
  await this.resumeRecording(call, sensitiveData);
142
- return this.exitStep('unrecognized', this.exitChoiceData('dtmf', {
143
- digit
144
- }));
144
+ return this.exitStep('unrecognized', this.exitChoiceData('dtmf', { digit }), longRecognition);
145
145
  }
146
146
  case 'recognition': {
147
147
  const params = event.params;
@@ -166,7 +166,7 @@ class Choice extends voice_1.default {
166
166
  });
167
167
  await this.resumeRecording(call, sensitiveData);
168
168
  // There might be hooks after this step which we will try to avoid
169
- return this.exitStep(exitId, this.exitChoiceData('voice', params));
169
+ return this.exitStep(exitId, this.exitChoiceData('voice', params), longRecognition);
170
170
  }
171
171
  else if (this.rptsHasMore({ repromptsList }) &&
172
172
  (usePromptsTriggers ? isRepromptTrigger(phrases, this.data.promptsTriggers) : true)) {
@@ -197,7 +197,7 @@ class Choice extends voice_1.default {
197
197
  });
198
198
  await this.resumeRecording(call, sensitiveData);
199
199
  // We might end up in same session
200
- return this.exitStep('unrecognized', this.exitChoiceData('voice', params));
200
+ return this.exitStep('unrecognized', this.exitChoiceData('voice', params), longRecognition);
201
201
  }
202
202
  case 'timeout': {
203
203
  if (this.rptsHasMore({ repromptsList })) {
@@ -225,7 +225,7 @@ class Choice extends voice_1.default {
225
225
  });
226
226
  await this.resumeRecording(call, sensitiveData);
227
227
  // We might end up in same session
228
- return this.exitStep('no reply', {});
228
+ return this.exitStep('no reply', {}, longRecognition);
229
229
  }
230
230
  case 'hangup': {
231
231
  await this.handleHangup(call);
@@ -37,7 +37,6 @@ export default class GlobalCommand extends VoiceStep<Partial<INPUT>, OUTPUT, EVE
37
37
  worker(): Promise<void>;
38
38
  hangup(call: IVoiceCall): Promise<unknown>;
39
39
  exitThread(event: ITypedEvent<EVENT>, type: string, stepExit: string): Promise<void>;
40
- exitToThread(): void;
41
40
  buildGrammar(call: IVoiceCall, choices: TODO[]): Promise<any>;
42
41
  replyAck(eventParams: any): void;
43
42
  }
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const types_1 = require("@onereach/flow-sdk/dst/types");
6
6
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
- const nanoid_1 = require("nanoid");
8
7
  const voice_1 = tslib_1.__importDefault(require("./voice"));
9
8
  class GlobalCommand extends voice_1.default {
10
9
  get isGlobal() {
@@ -81,10 +80,13 @@ class GlobalCommand extends voice_1.default {
81
80
  this.triggers.otherwise(async () => {
82
81
  await this.initGrammar(call);
83
82
  });
84
- this.triggers.hook({ name: "waitEnd" /* ACTION.waitEnd */, thread: types_1.MAIN_THREAD_ID, sync: true }, async () => {
85
- // todo: hook once
86
- delete this.waits['@waitEnd']; // TODO is there beter way to unsubscribe?
87
- delete this.waits.waitEnd; // TODO is there beter way to unsubscribe?
83
+ this.triggers.hook({ name: "waitEnd" /* ACTION.waitEnd */, thread: types_1.MAIN_THREAD_ID, sync: true, times: 1 }, async () => {
84
+ // TODO: this is not required in newer versions, delete in v6.1+
85
+ const allowAck = this.canVoicerAck(call);
86
+ if (!allowAck) {
87
+ delete this.waits['@waitEnd'];
88
+ delete this.waits.waitEnd;
89
+ }
88
90
  if (!call.ended) {
89
91
  this.state.passive = true;
90
92
  await this.sendCommands(call, [{ name: 'grammar', params: {} }]);
@@ -116,6 +118,7 @@ class GlobalCommand extends voice_1.default {
116
118
  reportingSettingsKey: 'transcript',
117
119
  actionFromBot: false
118
120
  });
121
+ await this.resumeRecording(call, { muteStep: true, muteUser: false, muteBot: false });
119
122
  return await this.exitThread(event, 'digit', exitId);
120
123
  }
121
124
  else {
@@ -139,6 +142,7 @@ class GlobalCommand extends voice_1.default {
139
142
  reportingSettingsKey: 'transcript',
140
143
  actionFromBot: false
141
144
  });
145
+ await this.resumeRecording(call, { muteStep: true, muteUser: false, muteBot: false });
142
146
  return await this.exitThread(event, 'voice', exitId);
143
147
  }
144
148
  else {
@@ -209,7 +213,6 @@ class GlobalCommand extends voice_1.default {
209
213
  });
210
214
  }
211
215
  async exitThread(event, type, stepExit) {
212
- this.log.debug('exitThread', type, stepExit);
213
216
  const params = event.params;
214
217
  const result = { type };
215
218
  if (!lodash_1.default.isEmpty(params.tags)) {
@@ -232,24 +235,7 @@ class GlobalCommand extends voice_1.default {
232
235
  if (!lodash_1.default.isEmpty(params.callRecording)) {
233
236
  result.callRecording = params.callRecording;
234
237
  }
235
- const exitLabel = lodash_1.default.replace(this.getExitStepLabel(stepExit) ?? stepExit, /\W+/g, '');
236
- await this.process.runThread({
237
- id: `${exitLabel}_${(0, nanoid_1.nanoid)(8)}`,
238
- state: {
239
- name: this.exitToThread.name,
240
- direct: true,
241
- result: {
242
- conversation: this.conversation,
243
- conversationThreadId: this.dataThreadId,
244
- ...result
245
- },
246
- exitStep: stepExit,
247
- step: this.currentStepId
248
- }
249
- });
250
- }
251
- exitToThread() {
252
- this.thread.exitStep(this.state.exitStep, this.state.result);
238
+ await this.exitStepByThread(stepExit, result);
253
239
  }
254
240
  async buildGrammar(call, choices) {
255
241
  const { asr } = this.data;
package/dst/step.js CHANGED
@@ -128,7 +128,7 @@ class ConvStep extends step_1.default {
128
128
  async waitGlobEnd() {
129
129
  const gcThreadId = this.workerThreadId;
130
130
  // wait for glob thread end (if exists)
131
- this.triggers.hook({ name: "end" /* ACTION.end */, thread: gcThreadId }, async () => await this.onConvEnd());
131
+ this.triggers.hook({ name: "end" /* ACTION.end */, thread: gcThreadId, times: 1 }, async () => await this.onConvEnd());
132
132
  if (!this.process.getThread(gcThreadId))
133
133
  await this.onConvEnd();
134
134
  }
package/dst/voice.d.ts CHANGED
@@ -8,6 +8,7 @@ export interface SensitiveData {
8
8
  muteBot: boolean;
9
9
  }
10
10
  export interface IVoiceCall extends IConversationData {
11
+ /** voicer protocol version */
11
12
  vv: number;
12
13
  id: string;
13
14
  ended: boolean;
@@ -51,7 +52,9 @@ export default class VoiceStep<TIn = unknown, TOut = unknown, TParams extends Vo
51
52
  handleZombie?: boolean;
52
53
  }, TOut, TParams> {
53
54
  runBefore(): Promise<void>;
54
- exitStep(step: string, data?: any): any;
55
+ exitStep(exitId: string, data?: any, byThread?: boolean): any;
56
+ exitStepByThread(exitId: string, result: any): any;
57
+ exitToThread(): void;
55
58
  sendCommands({ id, type, callback }: IVoiceCall, commands: TODO[]): Promise<void>;
56
59
  handleDataThreadStart(): void;
57
60
  handleDataThreadEnd(): void;
package/dst/voice.js CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.VoiceStepError = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
7
+ const nanoid_1 = require("nanoid");
7
8
  const types_1 = require("@onereach/flow-sdk/dst/types");
8
9
  const uuid_1 = require("uuid");
9
10
  // TODO: !!!!! import ConvStep from '@onereach/step-conversation/dst/step' !!!!!
@@ -22,11 +23,34 @@ class VoiceStep extends step_1.default {
22
23
  await this.handleHeartbeat(this.cache);
23
24
  }
24
25
  }
25
- exitStep(step, data) {
26
+ exitStep(exitId, data, byThread = false) {
27
+ if (byThread)
28
+ return this.exitStepByThread(exitId, data);
26
29
  if (this.cache != null) {
27
30
  this.handleDataThreadEnd();
28
31
  }
29
- return super.exitStep(step, data);
32
+ return super.exitStep(exitId, data);
33
+ }
34
+ exitStepByThread(exitId, result) {
35
+ this.log.debug('exitStepByThread', exitId, result);
36
+ const exitLabel = lodash_1.default.replace(this.getExitStepLabel(exitId) ?? exitId, /\W+/g, '');
37
+ return this.process.runThread({
38
+ id: `${exitLabel}_${(0, nanoid_1.nanoid)(8)}`,
39
+ state: {
40
+ name: this.exitToThread.name,
41
+ direct: true,
42
+ result: {
43
+ conversation: this.conversation,
44
+ conversationThreadId: this.dataThreadId,
45
+ ...result
46
+ },
47
+ exitStep: exitId,
48
+ step: this.currentStepId
49
+ }
50
+ });
51
+ }
52
+ exitToThread() {
53
+ this.thread.exitStep(this.state.exitStep, this.state.result);
30
54
  }
31
55
  async sendCommands({ id, type, callback }, commands) {
32
56
  if (lodash_1.default.isEmpty(commands))
@@ -62,7 +86,8 @@ class VoiceStep extends step_1.default {
62
86
  dataThread.waits[`thread/end/${this.thread.id}`] = {};
63
87
  return;
64
88
  }
65
- this.triggers.hook({ name: "ending" /* ACTION.ending */, thread: this.dataThreadId, sync: true }, async () => {
89
+ this.triggers.hook({ name: "ending" /* ACTION.ending */, thread: this.dataThreadId, sync: true, times: 1 }, async () => {
90
+ // TODO: this is not required in newer versions, delete in v6.1+
66
91
  delete this.waits['@ending'];
67
92
  delete this.waits.ending;
68
93
  if (dataThread.state.name === this.sleepUntilHangup.name) {
@@ -145,29 +170,34 @@ class VoiceStep extends step_1.default {
145
170
  .value();
146
171
  }
147
172
  async pauseRecording(call, command, sensitiveData) {
148
- await this.sendCommands(call, [
149
- ...call.recordCall && sensitiveData?.muteStep
150
- ? [{
151
- name: 'stop-record-session',
152
- params: {
153
- muteUser: sensitiveData.muteUser,
154
- muteBot: sensitiveData.muteBot
155
- }
156
- }]
157
- : [],
158
- command
159
- ]);
160
- }
161
- async resumeRecording(call, sensitiveData) {
173
+ const commands = [command];
162
174
  if (call.recordCall && sensitiveData?.muteStep) {
163
- await this.sendCommands(call, [{
164
- name: 'resume-record-session',
175
+ if (call.vv >= 2) {
176
+ // newer voicer version automaically should stop/resume session recording
177
+ command.params.sensitiveData = sensitiveData;
178
+ }
179
+ else {
180
+ commands.unshift({
181
+ name: 'stop-record-session',
165
182
  params: {
166
183
  muteUser: sensitiveData.muteUser,
167
184
  muteBot: sensitiveData.muteBot
168
185
  }
169
- }]);
186
+ });
187
+ }
170
188
  }
189
+ await this.sendCommands(call, commands);
190
+ }
191
+ async resumeRecording(call, sensitiveData) {
192
+ if (!call.recordCall || !sensitiveData?.muteStep || call.vv >= 2)
193
+ return;
194
+ await this.sendCommands(call, [{
195
+ name: 'resume-record-session',
196
+ params: {
197
+ muteUser: sensitiveData.muteUser,
198
+ muteBot: sensitiveData.muteBot
199
+ }
200
+ }]);
171
201
  }
172
202
  async transcript(call, data = {}) {
173
203
  const { previousTranscriptId = call.lastTranscriptId, action, keyPress, message, voiceProcessResult, reportingSettingsKey, sections, reprompt, recording, conferenceId, actionFromBot = false } = data;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onereach/step-voice",
3
- "version": "6.0.15",
3
+ "version": "6.0.17",
4
4
  "author": "Roman Zolotarov <roman.zolotarov@onereach.com>",
5
5
  "contributors": [
6
6
  "Roman Zolotarov",
@@ -19,7 +19,7 @@
19
19
  "uuid": "^9.0.0"
20
20
  },
21
21
  "devDependencies": {
22
- "@onereach/flow-sdk": "^3.2.16",
22
+ "@onereach/flow-sdk": "^3.2.45",
23
23
  "@onereach/step-voice": "./test",
24
24
  "@swc/cli": "^0.1.62",
25
25
  "@swc/core": "^1.3.57",