@onereach/step-voice 5.0.9-fixsubflow.0 → 5.0.9-fixsubflow.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.
package/dst/Cancel.js CHANGED
@@ -4,14 +4,14 @@ const tslib_1 = require("tslib");
4
4
  const voice_1 = tslib_1.__importDefault(require("./voice"));
5
5
  class VoiceCancel extends voice_1.default {
6
6
  async runStep() {
7
- const conv = await this.getConversation();
8
- if (conv.lcl.length <= 1) {
9
- this.log.debug('nothing to cancel');
10
- this.exitStep('next');
11
- return;
12
- }
7
+ // const conv = await this.getConversation()
8
+ // if (conv.lcl.length <= 1) {
9
+ // this.log.debug('nothing to cancel')
10
+ // this.exitStep('next')
11
+ // return
12
+ // }
13
13
  const call = await this.fetchData();
14
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
14
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
15
15
  switch (event.params.type) {
16
16
  case 'hangup':
17
17
  await this.handleHangup(call);
@@ -19,7 +19,7 @@ class VoiceCancel extends voice_1.default {
19
19
  case 'error':
20
20
  return this.throwError(event.params.error);
21
21
  case 'cancel': {
22
- await this.popConvStep(); // pop current command (cancel)
22
+ // await this.popConvStep() // pop current command (cancel)
23
23
  await this.cancel(); // cancel previous command
24
24
  return this.exitStep('next');
25
25
  }
package/dst/Choice.js CHANGED
@@ -93,7 +93,7 @@ class Choice extends voice_1.default {
93
93
  }
94
94
  };
95
95
  // There's a specific need to do so. There might be ${variable} section
96
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
96
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
97
97
  const reportingSettingsKey = this.rptsStarted ? 'transcriptRepromptResponse' : 'transcriptResponse';
98
98
  switch (event.params.type) {
99
99
  case 'digit':
@@ -231,9 +231,9 @@ class Choice extends voice_1.default {
231
231
  await this.handleHangup(call);
232
232
  return await this.waitConvEnd();
233
233
  }
234
- // case 'cancel': {
235
- // return this.data.handleCancel === true && this.exitStep('cancel')
236
- // }
234
+ case 'cancel': {
235
+ return this.data.handleCancel === true && this.exitStep('cancel');
236
+ }
237
237
  case 'error':
238
238
  return this.throwError(event.params.error);
239
239
  default:
@@ -7,7 +7,7 @@ class ConferenceDial extends voice_1.default {
7
7
  async runStep() {
8
8
  const call = await this.fetchData();
9
9
  this.triggers
10
- .once(`in/voice/${call.id}/event`, async (event) => {
10
+ .local(`in/voice/${call.id}/event`, async (event) => {
11
11
  switch (event.params.type) {
12
12
  case 'hangup':
13
13
  await this.handleHangup(call);
@@ -84,7 +84,7 @@ class CustomVoiceInput extends voice_1.default {
84
84
  }
85
85
  };
86
86
  // There's a specific need to do so. There might be ${variable} section
87
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
87
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
88
88
  const reportingSettingsKey = this.rptsStarted ? 'transcriptRepromptResponse' : 'transcriptResponse';
89
89
  switch (event.params.type) {
90
90
  // digit recognition removed
@@ -177,9 +177,9 @@ class CustomVoiceInput extends voice_1.default {
177
177
  await this.handleHangup(call);
178
178
  return await this.waitConvEnd();
179
179
  }
180
- // case 'cancel': {
181
- // return this.data.handleCancel === true && this.exitStep('cancel')
182
- // }
180
+ case 'cancel': {
181
+ return this.data.handleCancel === true && this.exitStep('cancel');
182
+ }
183
183
  case 'error':
184
184
  return this.throwError(event.params.error);
185
185
  default:
@@ -25,13 +25,14 @@ interface EVENT extends VoiceEvent {
25
25
  phrases?: TODO[];
26
26
  tags?: string[];
27
27
  out?: string;
28
+ background?: boolean;
28
29
  }
29
30
  export default class GlobalCommand extends VoiceStep<Partial<INPUT>, OUTPUT, EVENT> {
30
31
  get isGlobal(): boolean;
31
32
  get useQueue(): boolean;
32
33
  runStep(): Promise<void>;
33
34
  initGrammar(): Promise<void>;
34
- globThread(): Promise<void>;
35
+ _worker(): Promise<void>;
35
36
  hangup(call: IVoiceCall): Promise<unknown>;
36
37
  exitThread(event: ITypedEvent<EVENT>, type: string, stepExit: string): Promise<void>;
37
38
  onAwake(): Promise<void>;
@@ -7,29 +7,12 @@ const nanoid_1 = require("nanoid");
7
7
  const voice_1 = tslib_1.__importDefault(require("./voice"));
8
8
  class GlobalCommand extends voice_1.default {
9
9
  get isGlobal() {
10
- return this.thread.id === this.globalThreadId;
10
+ return true;
11
11
  }
12
12
  get useQueue() {
13
- return this.isGlobal;
13
+ return this.isWorker;
14
14
  }
15
15
  async runStep() {
16
- const globThread = this.process.newThread(this.globalThreadId, thread => {
17
- thread.state = {
18
- name: 'globThread',
19
- step: this.step.id,
20
- thread: this.dataThreadId
21
- };
22
- });
23
- if (globThread.isNewThread) {
24
- globThread.activate();
25
- await globThread.run();
26
- }
27
- else {
28
- globThread.state.step = this.step.id;
29
- }
30
- // refresh cache after global thread was started
31
- this._clearCache();
32
- await this.initGrammar();
33
16
  this.exitStep('no commands');
34
17
  }
35
18
  async initGrammar() {
@@ -62,31 +45,22 @@ class GlobalCommand extends voice_1.default {
62
45
  }
63
46
  ]);
64
47
  }
65
- async globThread() {
48
+ async _worker() {
66
49
  const call = await this.fetchData();
67
- if (call.vv >= 1 && lodash_1.default.isFunction(this.triggers.hook)) {
68
- this.triggers.hook({ name: 'ending', thread: 'main' }, async () => {
69
- this.thread.background = call._conv.glb.length > 1;
70
- this.log.debug('gc set background', { bg: this.thread.background, len: call._conv.glb.length });
71
- });
72
- this.triggers.hook({ name: 'end', thread: 'main' }, async () => {
73
- this.log.debug('gc terminate', { bg: this.thread.background });
74
- if (await this.popConvStep()) {
75
- await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'onAwake' });
76
- }
50
+ this.triggers.otherwise(async () => {
51
+ await this.initGrammar();
52
+ });
53
+ this.triggers.hook({ name: 'end', thread: 'main' }, async () => {
54
+ await this.sendCommands(call, [{ name: 'grammar', params: {} }]);
55
+ if (!this.thread.ending)
77
56
  this.end();
78
- });
79
- // TODO remove this hack that prevents this.triggers.once execution below
80
- // currently it skips logic to override triggers (is there better way to do it?)
81
- if (this.event.name.startsWith('@end'))
82
- return;
83
- }
84
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
57
+ });
58
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
85
59
  event.processed = undefined;
86
60
  switch (event.params.type) {
87
61
  case 'hangup': {
88
62
  await this.hangup(call);
89
- await this.popConvStep();
63
+ // await this.popConvStep()
90
64
  await this.notifyConvEnd();
91
65
  return this.end();
92
66
  }
@@ -137,11 +111,21 @@ class GlobalCommand extends voice_1.default {
137
111
  return {};
138
112
  }
139
113
  case 'error': {
140
- const localHandler = await this.sendEventToStep({ toGlobal: false, event: { ...this.event, processed: undefined, action: 'local' } });
141
- if (!localHandler) {
142
- this.throwError(event.params.error);
143
- }
114
+ // const localHandler = await this.sendEventToStep({ toWorker: false, event: { ...this.event, processed: undefined, action: 'lcl' } })
115
+ // if (!localHandler) {
116
+ // this.throwError(event.params.error)
117
+ // }
118
+ this.event.processed = true;
119
+ break;
120
+ }
121
+ case 'cancel': {
122
+ this.event.processed = true;
123
+ this.end();
124
+ break;
125
+ }
126
+ case 'background': {
144
127
  this.event.processed = true;
128
+ this.thread.background = event.params.background ?? false;
145
129
  break;
146
130
  }
147
131
  default:
@@ -223,7 +207,7 @@ class GlobalCommand extends voice_1.default {
223
207
  async onAwake() {
224
208
  await super.onAwake();
225
209
  await this.initGrammar();
226
- await this.globThread();
210
+ await this.worker();
227
211
  this.triggers.refreshAll();
228
212
  }
229
213
  exitToThread() {
package/dst/Hangup.js CHANGED
@@ -5,16 +5,16 @@ const voice_1 = tslib_1.__importDefault(require("./voice"));
5
5
  class Hangup extends voice_1.default {
6
6
  async runStep() {
7
7
  const call = await this.fetchData();
8
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
8
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
9
9
  switch (event.params.type) {
10
10
  case 'hangup':
11
11
  await this.handleHangup(call);
12
12
  return this.exitStep('next');
13
13
  case 'error':
14
14
  return this.throwError(event.params.error);
15
- // case 'cancel': {
16
- // return this.data.handleCancel === true && this.exitStep('cancel')
17
- // }
15
+ case 'cancel': {
16
+ return this.data.handleCancel === true && this.exitStep('cancel');
17
+ }
18
18
  default:
19
19
  return this.exitFlow();
20
20
  }
@@ -31,9 +31,9 @@ class InitiateCall extends voice_1.default {
31
31
  }
32
32
  });
33
33
  }
34
- const convData = { id: callId, type: 'voicer', vv: 0 };
34
+ const convData = { id: callId, type: 'voicer', vv: 0 }; // vv - voicer version
35
35
  await this.startConversation(convData);
36
- await this.pushConvStep();
36
+ // await this.pushConvStep()
37
37
  this.gotoState('waitForCall');
38
38
  }
39
39
  async onAwake() {
@@ -49,7 +49,7 @@ class InitiateCall extends voice_1.default {
49
49
  async waitForCall() {
50
50
  const { asr, tts, from: botNumber, endUserNumber, sipHost, sipUser, sipPassword, timeout, headers, enableSpoofCallerId, spoofCallerId, isAMD, otherCallRef, otherCallRefThread } = this.data;
51
51
  const call = await this.fetchData();
52
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
52
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
53
53
  switch (event.params.type) {
54
54
  case 'is_flow_ready': {
55
55
  // TODO this.exitFlow({is_ready : true}) should be enough
@@ -66,7 +66,7 @@ class InitiateCall extends voice_1.default {
66
66
  return this.exitFlow();
67
67
  }
68
68
  case 'call': {
69
- const call = {
69
+ const newCall = {
70
70
  headers: event.params.headers,
71
71
  ...event.params.channel,
72
72
  asr: asr.getSettings(),
@@ -74,15 +74,17 @@ class InitiateCall extends voice_1.default {
74
74
  botNumber: event.params.channel.to ?? 'unknown',
75
75
  endUserNumber
76
76
  };
77
- delete call.from;
78
- delete call.to;
79
- await this.startConversation(call);
80
- await this.transcript(call, {
77
+ delete newCall.from;
78
+ delete newCall.to;
79
+ newCall._conv = call._conv;
80
+ await this.startConversation(newCall);
81
+ // await this.popConvStep()
82
+ await this.transcript(newCall, {
81
83
  action: 'Call Start',
82
84
  reportingSettingsKey: 'transcript',
83
85
  actionFromBot: true
84
86
  });
85
- await this.sendCommands(call, [
87
+ await this.sendCommands(newCall, [
86
88
  ...isAMD
87
89
  ? [{
88
90
  name: 'start-avmd',
@@ -102,6 +104,11 @@ class InitiateCall extends voice_1.default {
102
104
  ]);
103
105
  return this.exitStep('success');
104
106
  }
107
+ case 'hangup': {
108
+ await this.handleHangup(call);
109
+ this.end();
110
+ break;
111
+ }
105
112
  case 'error': {
106
113
  const error = lodash_1.default.get(event, 'params.error.originateStatus');
107
114
  const errorCall = {
@@ -7,7 +7,7 @@ class JoinConference extends voice_1.default {
7
7
  async runStep() {
8
8
  const call = await this.fetchData();
9
9
  const { conferenceName, stayInConference } = this.data;
10
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
10
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
11
11
  switch (event.params.type) {
12
12
  case 'hangup':
13
13
  await this.handleHangup(call);
@@ -66,7 +66,7 @@ class KeypadInput extends voice_1.default {
66
66
  }
67
67
  const repromptsList = this.buildReprompts({ prompts: this.data.prompts, allowKeypadBargeIn: keypadBargeIn });
68
68
  const speechSections = this.buildSections({ sections: this.data.audio, textType, ttsSettings, allowKeypadBargeIn: keypadBargeIn });
69
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
69
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
70
70
  const reportingSettingsKey = this.rptsStarted ? 'transcriptRepromptResponse' : 'transcriptResponse';
71
71
  switch (event.params.type) {
72
72
  case 'digits': {
@@ -148,9 +148,9 @@ class KeypadInput extends voice_1.default {
148
148
  await this.handleHangup(call);
149
149
  return await this.waitConvEnd();
150
150
  }
151
- // case 'cancel': {
152
- // return this.data.handleCancel === true && this.exitStep('cancel')
153
- // }
151
+ case 'cancel': {
152
+ return this.data.handleCancel === true && this.exitStep('cancel');
153
+ }
154
154
  case 'error':
155
155
  return this.throwError(event.params.error);
156
156
  default:
@@ -7,7 +7,7 @@ class KickFromConference extends voice_1.default {
7
7
  async runStep() {
8
8
  const call = await this.fetchData();
9
9
  const { room, user } = this.data;
10
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
10
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
11
11
  switch (event.params.type) {
12
12
  case 'hangup':
13
13
  await this.handleHangup(call);
@@ -8,7 +8,7 @@ class SayMessage extends voice_1.default {
8
8
  async runStep() {
9
9
  const { audio, textType, tts, sensitiveData } = this.data;
10
10
  const call = await this.fetchData();
11
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
11
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
12
12
  switch (event.params.type) {
13
13
  case 'hangup':
14
14
  await this.handleHangup(call);
@@ -18,10 +18,9 @@ class SayMessage extends voice_1.default {
18
18
  return this.exitStep('next');
19
19
  case 'error':
20
20
  return this.throwError(event.params.error);
21
- //
22
- // case 'cancel': {
23
- // return this.data.handleCancel === true && this.exitStep('cancel')
24
- // }
21
+ case 'cancel': {
22
+ return this.data.handleCancel === true && this.exitStep('cancel');
23
+ }
25
24
  default:
26
25
  return this.exitFlow();
27
26
  }
@@ -1,4 +1,4 @@
1
- import VoiceStep from "./voice";
1
+ import VoiceStep from './voice';
2
2
  interface INPUT {
3
3
  dtmf: string;
4
4
  tonesDuration: string;
@@ -7,23 +7,23 @@ const schema = joi_1.default.object().keys({
7
7
  dtmf: joi_1.default
8
8
  .string()
9
9
  .required()
10
- .replace(/\s+/, "")
11
- .pattern(/^[0-9A-DwW*#]+$/, "symbols")
10
+ .replace(/\s+/, '')
11
+ .pattern(/^[0-9A-DwW*#]+$/, 'symbols')
12
12
  .messages({
13
- "string.pattern.symbols": "{{#label}} should only contain symbols like: 0-9, *, #, A-D, W, w",
13
+ 'string.pattern.symbols': '{{#label}} should only contain symbols like: 0-9, *, #, A-D, W, w'
14
14
  }),
15
15
  tonesDuration: joi_1.default.number().required().min(200).max(5000).messages({
16
- "number.min": "{{#label}} should be between 200 and 5000 milliseconds",
17
- "number.max": "{{#label}} should be between 200 and 5000 milliseconds",
16
+ 'number.min': '{{#label}} should be between 200 and 5000 milliseconds',
17
+ 'number.max': '{{#label}} should be between 200 and 5000 milliseconds'
18
18
  }),
19
19
  beforeDelay: joi_1.default.number().required().min(0).max(5000).messages({
20
- "number.min": "{{#label}} should be between 0 and 5000 milliseconds",
21
- "number.max": "{{#label}} should be between 0 and 5000 milliseconds",
20
+ 'number.min': '{{#label}} should be between 0 and 5000 milliseconds',
21
+ 'number.max': '{{#label}} should be between 0 and 5000 milliseconds'
22
22
  }),
23
23
  afterDelay: joi_1.default.number().required().min(0).max(5000).messages({
24
- "number.min": "{{#label}} should be between 0 and 5000 milliseconds",
25
- "number.max": "{{#label}} should be between 0 and 5000 milliseconds",
26
- }),
24
+ 'number.min': '{{#label}} should be between 0 and 5000 milliseconds',
25
+ 'number.max': '{{#label}} should be between 0 and 5000 milliseconds'
26
+ })
27
27
  });
28
28
  class SendDTMF extends voice_1.default {
29
29
  async runStep() {
@@ -33,31 +33,31 @@ class SendDTMF extends voice_1.default {
33
33
  dtmf,
34
34
  tonesDuration,
35
35
  beforeDelay,
36
- afterDelay,
36
+ afterDelay
37
37
  });
38
38
  if (validationError != null)
39
39
  throw new Error(validationError.message);
40
40
  const command = {
41
- name: "send-dtmf",
41
+ name: 'send-dtmf',
42
42
  params: {
43
43
  dtmf,
44
44
  tonesDuration: +tonesDuration,
45
45
  beforeDelay: +beforeDelay,
46
- afterDelay: +afterDelay,
47
- },
46
+ afterDelay: +afterDelay
47
+ }
48
48
  };
49
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
49
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
50
50
  // if (channel.callback.id)
51
51
  // await this.mergeFields[_conversation].set(
52
52
  // this.takeCallback(),
53
53
  // "callback"
54
54
  // );
55
55
  switch (event.params.type) {
56
- case "hangup":
56
+ case 'hangup':
57
57
  return this.end();
58
- case "dtmf-sent":
59
- return this.exitStep("next");
60
- case "error":
58
+ case 'dtmf-sent':
59
+ return this.exitStep('next');
60
+ case 'error':
61
61
  return this.throwError(event.params.error);
62
62
  default:
63
63
  return this.exitFlow();
@@ -65,11 +65,11 @@ class SendDTMF extends voice_1.default {
65
65
  });
66
66
  this.triggers.otherwise(async () => {
67
67
  await this.transcript(call, {
68
- reportingSettingsKey: "transcript",
69
- action: "Send DTMF",
68
+ reportingSettingsKey: 'transcript',
69
+ action: 'Send DTMF',
70
70
  actionFromBot: true,
71
71
  keyPress: dtmf,
72
- message: dtmf,
72
+ message: dtmf
73
73
  });
74
74
  await this.sendCommands(call, [command]);
75
75
  return this.exitFlow();
package/dst/Transfer.js CHANGED
@@ -6,7 +6,7 @@ const voice_1 = tslib_1.__importDefault(require("./voice"));
6
6
  class Transfer extends voice_1.default {
7
7
  async runStep() {
8
8
  const call = await this.fetchData();
9
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
9
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
10
10
  switch (event.params.type) {
11
11
  case 'bridge': {
12
12
  await this.transcript(call, {
@@ -23,7 +23,7 @@ class Recording extends voice_1.default {
23
23
  sections
24
24
  }
25
25
  };
26
- this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
26
+ this.triggers.local(`in/voice/${call.id}/event`, async (event) => {
27
27
  const url = event.params.mediaPath;
28
28
  switch (event.params.type) {
29
29
  case 'hangup':
package/dst/step.d.ts CHANGED
@@ -1,35 +1,40 @@
1
1
  import Step from '@onereach/flow-sdk/dst/step';
2
- import { IThreadId, IEvent, IStepResult, IMergeField, ISessionId } from '@onereach/flow-sdk/dst/types';
3
- import { IConversationStep } from '@onereach/step-conversation/dst/types';
2
+ import { IThreadId, IMergeField } from '@onereach/flow-sdk/dst/types';
4
3
  export interface ConvStepDataIn {
5
4
  conversation?: string | IMergeField;
6
5
  conversationThread?: IThreadId;
7
6
  }
8
- interface IConversation {
9
- glb: IConversationStep[];
10
- lcl: IConversationStep[];
11
- sid?: ISessionId;
7
+ export interface IConversation {
8
+ dummy?: never;
12
9
  }
13
- interface IConversationData {
10
+ export interface IConversationData {
14
11
  _conv: IConversation;
15
12
  }
16
- export default class ConvStep<TData = unknown, TIn = unknown, TOut = unknown, TParams = any> extends Step<TIn & ConvStepDataIn, TOut, TParams> {
13
+ export declare const enum ConvThreadType {
14
+ lcl = "lcl",
15
+ glb = "glb",
16
+ wrk = "wrk"
17
+ }
18
+ export default class ConvStep<TData extends IConversationData, TIn = unknown, TOut = unknown, TParams = any> extends Step<TIn & ConvStepDataIn, TOut, TParams> {
17
19
  private convDataCache?;
18
- get cache(): TData & IConversationData | undefined;
20
+ _worker(_data: IConversationData): Promise<void>;
21
+ get cache(): TData | undefined;
19
22
  get conversation(): string | IMergeField;
20
23
  get conversationId(): string;
21
- get globalThreadId(): string;
24
+ get workerThreadId(): string;
22
25
  /** id of the thread where conversation data is stored */
23
26
  get dataThreadId(): IThreadId;
24
27
  get isGlobal(): boolean;
28
+ get threadType(): ConvThreadType;
29
+ get isWorker(): boolean;
25
30
  get useQueue(): boolean;
26
- fetchData(): Promise<TData & IConversationData>;
31
+ fetchData(): Promise<TData>;
27
32
  getConversation(): Promise<IConversation>;
28
33
  updateData(): Promise<void>;
29
34
  hasConversation(): Promise<boolean>;
35
+ protected worker(): Promise<void>;
30
36
  runBefore(): Promise<void>;
31
37
  runAfter(): Promise<void>;
32
- hasActiveGlobal(): Promise<boolean>;
33
38
  waitForConversation(): Promise<void>;
34
39
  protected onSkipEvent(): Promise<void>;
35
40
  cancel(): Promise<void>;
@@ -46,22 +51,12 @@ export default class ConvStep<TData = unknown, TIn = unknown, TOut = unknown, TP
46
51
  onConvEnd(): Promise<void>;
47
52
  pause(): Promise<void>;
48
53
  resume(): Promise<void>;
49
- protected sendEventToStep({ toGlobal, toStep, action, event }: {
50
- action?: string;
51
- event?: IEvent;
52
- toStep?: IConversationStep;
53
- toGlobal?: boolean;
54
- }): Promise<IStepResult | undefined>;
55
- activeStep(isGlobal?: boolean): Promise<IConversationStep | undefined>;
56
54
  /** @returns true if current conv is active */
57
- protected pushConvStep(): Promise<boolean>;
58
55
  /** @returns true if current conv was active */
59
- protected popConvStep(popStep?: IConversationStep): Promise<boolean>;
60
56
  startConversation(data: TData, { thread }?: {
61
57
  thread?: IThreadId;
62
58
  }): Promise<void>;
63
- protected _fetchCache(): Promise<(TData & IConversationData) | undefined>;
59
+ protected _fetchCache(): Promise<(TData) | undefined>;
64
60
  protected _clearCache(): void;
65
61
  protected _refreshCache(): Promise<void>;
66
62
  }
67
- export {};
package/dst/step.js CHANGED
@@ -3,7 +3,17 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const step_1 = tslib_1.__importDefault(require("@onereach/flow-sdk/dst/step"));
6
+ // eslint-disable-next-line @typescript-eslint/no-redeclare
7
+ // interface IConversationStep {
8
+ // key: string
9
+ // trd: string
10
+ // typ?: string
11
+ // name?: string // for debug
12
+ // label?: string // for debug
13
+ // step?: string // for debug
14
+ // }
6
15
  class ConvStep extends step_1.default {
16
+ async _worker(_data) { }
7
17
  get cache() {
8
18
  return this.convDataCache;
9
19
  }
@@ -25,7 +35,7 @@ class ConvStep extends step_1.default {
25
35
  return convOrName;
26
36
  throw new Error(`invalid conversation ${JSON.stringify(conv)}`);
27
37
  }
28
- get globalThreadId() {
38
+ get workerThreadId() {
29
39
  return `G:${this.conversationId}`;
30
40
  }
31
41
  /** id of the thread where conversation data is stored */
@@ -36,6 +46,14 @@ class ConvStep extends step_1.default {
36
46
  get isGlobal() {
37
47
  return false;
38
48
  }
49
+ get threadType() {
50
+ return this.thread.id === this.workerThreadId
51
+ ? (this.isGlobal ? "glb" /* ConvThreadType.glb */ : "wrk" /* ConvThreadType.wrk */)
52
+ : "lcl" /* ConvThreadType.lcl */;
53
+ }
54
+ get isWorker() {
55
+ return this.threadType !== "lcl" /* ConvThreadType.lcl */;
56
+ }
39
57
  get useQueue() {
40
58
  return true;
41
59
  }
@@ -58,13 +76,18 @@ class ConvStep extends step_1.default {
58
76
  async hasConversation() {
59
77
  return (await this._fetchCache())?._conv != null;
60
78
  }
79
+ async worker() {
80
+ this.triggers.local('_conv');
81
+ if (this.cache == null)
82
+ return;
83
+ await this._worker(this.cache);
84
+ }
61
85
  async runBefore() {
62
86
  await super.runBefore();
63
87
  if (this.thread.ending)
64
88
  return;
65
89
  // ensure cache is reset between step runs, just in case
66
90
  this._clearCache();
67
- this.triggers.local('_conv');
68
91
  if (!await this.hasConversation())
69
92
  return;
70
93
  this.log.debug('conv.runBefore', { state: this.state, conv: this.convDataCache?._conv });
@@ -76,46 +99,71 @@ class ConvStep extends step_1.default {
76
99
  this.state.name = this.event.action;
77
100
  return;
78
101
  }
79
- if (this.useQueue && !await this.pushConvStep()) {
80
- return await this.waitForConversation();
81
- }
82
- // if (this.event.action == null && !this.event.processed) {
83
- // if (this.isGlobal) return
84
- // const hasGlobal = await this.sendEventToStep({ event: { ...this.event, action: 'global' }, toGlobal: true }) // redirect to global (1)
85
- // if (hasGlobal != null) {
86
- // this.log.debug('conv.sendEventToStep toGlobal', { hasGlobal })
87
- // this.state.prevSkipName = this.state.name
88
- // this.state.name = 'onSkipEvent'
89
- // }
90
- // }
91
- // if (this.isGlobal && this.event.action === 'global') {
92
- // // it was redirected from (1)
102
+ console.log('BEFORE', {
103
+ event: this.event,
104
+ state: this.state,
105
+ name: this.constructor.name,
106
+ thread: this.thread.id,
107
+ ...this.cache?._conv
108
+ });
109
+ // if (this.useQueue && !await this.pushConvStep()) {
110
+ // return await this.waitForConversation()
93
111
  // }
94
- // await this.setLocalTriggers()
95
- }
96
- async runAfter() {
97
- if (this.thread.ending)
112
+ if (this.cache == null)
98
113
  return;
99
- if (!await this.hasConversation())
100
- return await super.runAfter();
101
- const sid = this.cache?._conv.sid;
102
- if (sid != null && sid !== this.session.sessionId) {
103
- this.triggers.refreshAll();
104
- }
105
- if (this.thread.hasControlAction()) {
106
- // await this.popConvStep()
107
- if (await this.popConvStep()) {
108
- await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'onAwake' });
114
+ // const glb = this.cache._conv.glb
115
+ // if (!this.isGlobal && glb.length > 0 && glb[glb.length - 1].typ !== ConvThreadType.wrk) return
116
+ if (!this.isGlobal)
117
+ return;
118
+ if (this.thread.id !== this.workerThreadId) {
119
+ const worker = this.process.newThread(this.workerThreadId, thread => {
120
+ thread.state = {
121
+ name: 'worker',
122
+ step: this.step.id,
123
+ thread: this.dataThreadId
124
+ };
125
+ });
126
+ if (worker.isNewThread) {
127
+ worker.activate();
128
+ await worker.run();
129
+ await this._refreshCache();
130
+ }
131
+ else {
132
+ worker.state.step = this.step.id;
109
133
  }
110
134
  }
111
- if (this.isGlobal && !this.event.processed) {
112
- await this.sendEventToStep({ toGlobal: false, event: { ...this.event, processed: undefined, action: 'local' } });
113
- }
114
- await super.runAfter();
115
135
  }
116
- async hasActiveGlobal() {
117
- const globalLength = (await this.getConversation())?.glb?.length ?? 0;
118
- return globalLength > 0;
136
+ // public async runHandle (): Promise<unknown> {
137
+ // if (this.thread.ending || this.state.name === 'onConvEnd') {
138
+ // return await super.runHandle()
139
+ // }
140
+ // // const sid = this.cache?._conv.sid
141
+ // // if (sid != null && sid !== this.session.sessionId) {
142
+ // // this.triggers.refreshAll()
143
+ // // }
144
+ // return await super.runHandle()
145
+ // }
146
+ async runAfter() {
147
+ console.log('AFTER', {
148
+ name: this.constructor.name,
149
+ thread: this.thread.id,
150
+ ...this.cache?._conv
151
+ });
152
+ // if (this.thread.ending || this.state.name === 'onConvEnd') {
153
+ // return await super.runAfter()
154
+ // }
155
+ // if (!await this.hasConversation()) return await super.runAfter()
156
+ // if (this.thread.hasControlAction()) {
157
+ // // await this.popConvStep()
158
+ // if (await this.popConvStep()) {
159
+ // // TODO: this seems not to be necessary now or not?
160
+ // await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'onAwake' })
161
+ // }
162
+ // }
163
+ // if (this.isGlobal && !this.event.processed) {
164
+ // await this.sendEventToStep({ toGlobal: false, event: { ...this.event, processed: undefined, action: 'lcl' } })
165
+ // }
166
+ await super.runAfter();
119
167
  }
120
168
  // public async setLocalTriggers (): Promise<void> {
121
169
  // if (!this.isGlobal && await this.hasActiveGlobal()) {
@@ -142,15 +190,15 @@ class ConvStep extends step_1.default {
142
190
  delete this.state.prevSkipName;
143
191
  }
144
192
  async cancel() {
145
- if (this.isGlobal) {
193
+ if (this.isWorker) {
146
194
  this.log.debug('conv.cannot cancel global');
147
- return;
195
+ // return
148
196
  }
149
- const activeStep = await this.activeStep();
150
- await this.sendEventToStep({ event: { name: '_conv', action: 'onCancel', params: { byStep: this.step.label } }, toStep: activeStep });
197
+ // const activeStep = await this.activeStep(this.isWorker)
198
+ // await this.sendEventToStep({ event: { name: '_conv', action: 'onCancel', params: { byStep: this.step.label } }, toStep: activeStep })
151
199
  }
152
200
  async onCancel() {
153
- await this.popConvStep();
201
+ // await this.popConvStep()
154
202
  if (this.getExitStepId('cancel')) {
155
203
  this.log.debug('conv.cancel exit');
156
204
  this.exitStep('cancel');
@@ -165,11 +213,11 @@ class ConvStep extends step_1.default {
165
213
  async onAwake() {
166
214
  this.state.name = this.state.resumeState;
167
215
  this.state.resumeState = undefined;
168
- const conv = await this.getConversation();
169
- if (this.isGlobal && conv.glb.length === 1) {
170
- this.log.debug('make global thread foreground');
171
- this.thread.background = false;
172
- }
216
+ // const conv = await this.getConversation()
217
+ // if (this.isWorker && conv.glb.length === 1) {
218
+ // this.log.debug('make global thread foreground')
219
+ // this.thread.background = false
220
+ // }
173
221
  }
174
222
  async onPause() {
175
223
  this.state.resumeState = this.state.prevName;
@@ -180,22 +228,24 @@ class ConvStep extends step_1.default {
180
228
  this.state.resumeState = undefined;
181
229
  }
182
230
  async notifyConvEnd({ onlyLocal } = {}) {
183
- const conv = await this.getConversation();
184
- const lcl = conv.lcl.splice(0);
185
- const glb = onlyLocal ? [] : conv.glb.splice(0);
186
- if (lcl.length > 0 || glb.length > 0) {
187
- await this.updateData();
188
- for (const step of lcl) {
189
- await this.sendEventToStep({ toStep: step, action: 'onConvEnd' });
190
- }
191
- for (const step of glb) {
192
- await this.sendEventToStep({ toStep: step, action: 'onConvEnd' });
193
- }
194
- }
231
+ this.log.debug('notifyConvEnd', { onlyLocal });
232
+ // const conv = await this.getConversation()
233
+ // const lcl = conv.lcl.splice(0)
234
+ // const wrk = onlyLocal ? [] : conv.glb.splice(0)
235
+ // if (lcl.length > 0 || wrk.length > 0) {
236
+ // await this.updateData()
237
+ // for (const step of lcl) {
238
+ // await this.sendEventToStep({ toStep: step, action: 'onConvEnd' })
239
+ // }
240
+ // for (const step of wrk) {
241
+ // await this.sendEventToStep({ toStep: step, action: 'onConvEnd' })
242
+ // }
243
+ // }
195
244
  }
196
245
  async waitConvEnd() {
197
- const conversationThread = this.state.thread ?? this.thread.id;
198
- if (!this.isGlobal && conversationThread === this.thread.id && this.thread.id !== 'main') {
246
+ const workerThread = this.process.getThread(this.workerThreadId);
247
+ const workerData = await workerThread?.currentStep.resolveDataIn();
248
+ if (!this.isGlobal && this.thread.id !== 'main' && this.thread.id === workerData?.conversationThread) {
199
249
  this.log.debug('conv.wait for end', this.conversation);
200
250
  this.gotoState({ ...this.state, name: 'waitGlobEnd', direct: true });
201
251
  }
@@ -205,7 +255,7 @@ class ConvStep extends step_1.default {
205
255
  }
206
256
  }
207
257
  async waitGlobEnd() {
208
- const gcThreadId = this.globalThreadId;
258
+ const gcThreadId = this.workerThreadId;
209
259
  // wait for glob thread end (if exists)
210
260
  this.triggers.hook({ name: 'end', thread: gcThreadId }, async () => await this.onConvEnd());
211
261
  if (!this.process.getThread(gcThreadId))
@@ -228,7 +278,7 @@ class ConvStep extends step_1.default {
228
278
  }
229
279
  async resume() {
230
280
  // const conv = await this._getConversation()
231
- // if (conv.stk.length > 0) {
281
+ // if (conv.stk.length> 0) {
232
282
  // conv.lcl.unshift(...(conv.stk.pop() ?? [])) // restore que from stk
233
283
  // await this.updateData()
234
284
  // await this.sendEventToStep({ toGlobal: false, action: 'onResume' })
@@ -236,66 +286,96 @@ class ConvStep extends step_1.default {
236
286
  // this.log.debug('conv.nothing to resume')
237
287
  // }
238
288
  }
239
- async sendEventToStep({ toGlobal, toStep, action, event = { name: '_conv', action } }) {
240
- toStep || (toStep = await this.activeStep(toGlobal));
241
- if (toStep != null) {
242
- const target = { type: 'session', name: toStep.key, id: toStep.thread };
243
- return await this.thread.eventManager.emit(event, { invocationType: 'async', target });
244
- }
245
- return undefined;
246
- }
247
- async activeStep(isGlobal = this.isGlobal) {
248
- const conv = await this.getConversation();
249
- return isGlobal ? conv.glb[conv.glb.length - 1] : conv.lcl[conv.lcl.length - 1];
250
- }
289
+ // protected async sendEventToStep ({
290
+ // toGlobal = false,
291
+ // toStep,
292
+ // action,
293
+ // event = { name: '_conv', action }
294
+ // }: {
295
+ // action?: string
296
+ // event?: IEvent
297
+ // toStep?: IConversationStep
298
+ // toGlobal?: boolean
299
+ // }): Promise<IStepResult | undefined> {
300
+ // toStep ||= await this.activeStep(toGlobal)
301
+ // if (toStep != null) {
302
+ // const target = { type: 'session' as any, name: toStep.key, id: toStep.trd }
303
+ // return await this.thread.eventManager.emit(
304
+ // event,
305
+ // { invocationType: 'async', target }
306
+ // )
307
+ // }
308
+ // return undefined
309
+ // }
310
+ // async activeStep (toGlobal: boolean): Promise<IConversationStep | undefined> {
311
+ // const conv = await this.getConversation()
312
+ // const que = toGlobal ? conv.glb : conv.lcl
313
+ // if (que == null) return
314
+ // const lastItem = que[que.length - 1]
315
+ // if (toGlobal && lastItem?.typ === ConvThreadType.wrk) return undefined
316
+ // return que[que.length - 1]
317
+ // }
251
318
  /** @returns true if current conv is active */
252
- async pushConvStep() {
253
- const conv = await this.getConversation();
254
- const sessionKey = this.session.key;
255
- if (sessionKey == null)
256
- throw new Error('session is required');
257
- const que = this.isGlobal ? conv.glb : conv.lcl;
258
- const queIndex = que.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread);
259
- if (queIndex < 0) {
260
- await this.sendEventToStep({ toGlobal: this.isGlobal, event: { name: '_conv', action: 'onPause', params: { byStep: this.step.label } } });
261
- que.push({ key: sessionKey, thread: this.thread.id, name: this.constructor.name, label: this.label, step: this.id });
262
- await this.updateData();
263
- return true;
264
- }
265
- return queIndex === que.length - 1;
266
- }
319
+ // protected async pushConvStep (): Promise<boolean> {
320
+ // const conv = await this.getConversation()
321
+ // const sessionKey = this.session.key
322
+ // if (sessionKey == null) throw new Error('session is required')
323
+ // const que = this.isWorker ? conv.glb : conv.lcl
324
+ // const lastItem = que[que.length - 1]
325
+ // const lastItemType = lastItem?.typ
326
+ // if (lastItemType === ConvThreadType.wrk) {
327
+ // console.log('POP', lastItem)
328
+ // que.pop() // remove last item, since it's temporary
329
+ // // TODO triggers.refreshAll!
330
+ // }
331
+ // const queIndex = que.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.trd)
332
+ // if (queIndex < 0) {
333
+ // if (que.length > 0) {
334
+ // await this.sendEventToStep({ toGlobal: this.isGlobal, event: { name: '_conv', action: 'onPause', params: { byStep: this.step.label } } })
335
+ // }
336
+ // const type = this.threadType === ConvThreadType.wrk ? this.threadType : undefined
337
+ // que.push({ key: sessionKey, trd: this.thread.id, typ: type, name: this.constructor.name, label: this.label, step: this.id })
338
+ // console.log('PUSH', que[que.length - 1])
339
+ // await this.updateData()
340
+ // return true
341
+ // }
342
+ // // refresh data on step change
343
+ // que[queIndex].name = this.constructor.name
344
+ // que[queIndex].label = this.label
345
+ // que[queIndex].step = this.id
346
+ // return queIndex === que.length - 1
347
+ // }
267
348
  /** @returns true if current conv was active */
268
- async popConvStep(popStep) {
269
- const conv = await this.getConversation();
270
- const sessionKey = this.session.key;
271
- if (sessionKey == null)
272
- throw new Error('session is required');
273
- popStep || (popStep = { key: sessionKey, thread: this.thread.id });
274
- const que = this.isGlobal ? conv.glb : conv.lcl;
275
- const queIndex = que.findIndex(stp => stp.key === popStep?.key && stp.thread === popStep.thread);
276
- if (queIndex >= 0) {
277
- // console.log('SPLICE1', this.thread.id, this.step.label, que)
278
- que.splice(queIndex, 1);
279
- conv.sid = this.session.sessionId;
280
- await this.updateData();
281
- return queIndex === que.length;
282
- }
283
- // let updated = false
284
- // conv.stk.forEach((steps, stepsIdx) => {
285
- // const stpIdx = steps.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread)
286
- // if (stpIdx >= 0) {
287
- // if (steps.length > 1) steps.splice(stpIdx, 1)
288
- // else conv.stk.splice(stepsIdx, 1)
289
- // updated = true
290
- // }
291
- // })
292
- // if (updated) await this.updateData()
293
- return false;
294
- }
349
+ // protected async popConvStep (popStep?: IConversationStep): Promise<boolean> {
350
+ // const conv = await this.getConversation()
351
+ // const sessionKey = this.session.key
352
+ // if (sessionKey == null) throw new Error('session is required')
353
+ // popStep ||= { key: sessionKey, trd: this.thread.id }
354
+ // const que = this.isWorker ? conv.glb : conv.lcl
355
+ // const queIndex = que.findIndex(stp => stp.key === popStep?.key && stp.trd === popStep.trd)
356
+ // if (queIndex >= 0) {
357
+ // // console.log('SPLICE1', this.thread.id, this.step.label, que)
358
+ // que.splice(queIndex, 1)
359
+ // // conv.sid = this.session.sessionId
360
+ // await this.updateData()
361
+ // return queIndex === que.length
362
+ // }
363
+ // // let updated = false
364
+ // // conv.stk.forEach((steps, stepsIdx) => {
365
+ // // const stpIdx = steps.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread)
366
+ // // if (stpIdx>= 0) {
367
+ // // if (steps.length> 1) steps.splice(stpIdx, 1)
368
+ // // else conv.stk.splice(stepsIdx, 1)
369
+ // // updated = true
370
+ // // }
371
+ // // })
372
+ // // if (updated) await this.updateData()
373
+ // return false
374
+ // }
295
375
  async startConversation(data, { thread: _thread } = {}) {
296
376
  this.convDataCache = {
297
377
  ...data,
298
- _conv: {
378
+ _conv: data._conv ?? {
299
379
  glb: [],
300
380
  lcl: []
301
381
  }
package/dst/voice.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ICallback, IVoiceReporterTranscriptEventArgs, IPromtpSection } from '@onereach/flow-sdk/dst/types';
2
- import ConvStep from './step';
2
+ import ConvStep, { IConversationData } from './step';
3
3
  import BasicError from '@onereach/flow-sdk/dst/errors/base';
4
4
  export type TODO = any;
5
5
  export interface SensitiveData {
@@ -7,7 +7,7 @@ export interface SensitiveData {
7
7
  muteUser: boolean;
8
8
  muteBot: boolean;
9
9
  }
10
- export interface IVoiceCall {
10
+ export interface IVoiceCall extends IConversationData {
11
11
  vv: number;
12
12
  id: string;
13
13
  ended: boolean;
@@ -25,7 +25,7 @@ export interface IVoiceCall {
25
25
  from?: string;
26
26
  headers?: Record<string, string>;
27
27
  }
28
- export type EventType = 'hangup' | 'error' | 'cancel' | 'avm-detected' | 'recognition' | 'digit' | 'digits' | 'conference-start' | 'conference-end' | 'playback' | 'timeout' | 'record' | 'bridge' | 'bridge/ended' | 'is_flow_ready' | 'call' | 'dtmf-sent';
28
+ export type EventType = 'hangup' | 'error' | 'cancel' | 'background' | 'avm-detected' | 'recognition' | 'digit' | 'digits' | 'conference-start' | 'conference-end' | 'playback' | 'timeout' | 'record' | 'bridge' | 'bridge/ended' | 'is_flow_ready' | 'call' | 'dtmf-sent';
29
29
  export declare class VoiceStepError extends BasicError {
30
30
  }
31
31
  export type VoicerError = Error | string & {
package/dst/voice.js CHANGED
@@ -16,14 +16,15 @@ class VoiceStep extends step_1.default {
16
16
  async runBefore() {
17
17
  await super.runBefore();
18
18
  if (this.cache != null) {
19
- if (!this.event.processed && !this.isGlobal && this.event.params.global && this.event.action == null) {
20
- const hasGlobal = await this.sendEventToStep({ event: { ...this.event, action: 'global' }, toGlobal: true }); // redirect to global (1)
21
- if (hasGlobal != null) {
22
- this.log.debug('conv.sendEventToStep toGlobal', { hasGlobal });
23
- this.state.prevSkipName = this.state.name;
24
- this.state.name = 'onSkipEvent';
25
- }
26
- }
19
+ // if (!this.event.processed && this.event.action == null) {
20
+ // const toWorker = this.event.params.global
21
+ // const hasHandler = await this.sendEventToStep({ event: { ...this.event, action: toWorker ? 'wrk' : 'lcl' }, toWorker }) // redirect to global (1)
22
+ // if (hasHandler != null) {
23
+ // this.log.debug('hasWorker', { hasGlobal: hasHandler })
24
+ // this.state.prevSkipName = this.state.name
25
+ // this.state.name = 'onSkipEvent'
26
+ // }
27
+ // }
27
28
  await this.handleHeartbeat(this.cache);
28
29
  }
29
30
  }
@@ -35,10 +36,11 @@ class VoiceStep extends step_1.default {
35
36
  name: `out/voice/${type}`,
36
37
  params: {
37
38
  id,
38
- cancel: this.isGlobal ? undefined : true,
39
- async: this.isGlobal ? true : undefined,
39
+ cancel: this.isWorker ? undefined : true,
40
+ async: this.isWorker ? true : undefined,
40
41
  hbs: 99,
41
- commands
42
+ commands,
43
+ step: { key: this.session.key, trd: this.isGlobal ? this.workerThreadId : this.thread.id } // response should be sent to this session
42
44
  },
43
45
  reporting: this.session.getSessionRef()
44
46
  }, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onereach/step-voice",
3
- "version": "5.0.9-fixsubflow.0",
3
+ "version": "5.0.9-fixsubflow.10",
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.9",
22
+ "@onereach/flow-sdk": "^3.2.16",
23
23
  "@onereach/step-voice": "./test",
24
24
  "@swc/cli": "^0.1.62",
25
25
  "@swc/core": "^1.3.57",