@onereach/step-voice 5.0.9-fixsubflow.1 → 5.0.9-fixsubflow.3

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,12 +4,12 @@ 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
14
  this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
15
15
  switch (event.params.type) {
@@ -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
  }
@@ -31,7 +31,7 @@ export default class GlobalCommand extends VoiceStep<Partial<INPUT>, OUTPUT, EVE
31
31
  get useQueue(): boolean;
32
32
  runStep(): Promise<void>;
33
33
  initGrammar(): Promise<void>;
34
- globThread(): Promise<void>;
34
+ _worker(): Promise<void>;
35
35
  hangup(call: IVoiceCall): Promise<unknown>;
36
36
  exitThread(event: ITypedEvent<EVENT>, type: string, stepExit: string): Promise<void>;
37
37
  onAwake(): Promise<void>;
@@ -7,28 +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
16
  await this.initGrammar();
33
17
  this.exitStep('no commands');
34
18
  }
@@ -62,23 +46,26 @@ class GlobalCommand extends voice_1.default {
62
46
  }
63
47
  ]);
64
48
  }
65
- async globThread() {
49
+ async _worker() {
66
50
  const call = await this.fetchData();
67
51
  if (call.vv >= 1 && lodash_1.default.isFunction(this.triggers.hook)) {
68
- this.triggers.hook({ name: 'ending', thread: 'main' }, async () => {
52
+ this.triggers.hook({ name: 'waitEnd', thread: 'main', sync: true }, async () => {
69
53
  this.thread.background = call._conv.glb.length > 1;
70
- this.log.debug('gc set background', { bg: this.thread.background, len: call._conv.glb.length });
54
+ if (this.thread.background)
55
+ this.end();
71
56
  });
72
57
  this.triggers.hook({ name: 'end', thread: 'main' }, async () => {
73
58
  this.log.debug('gc terminate', { bg: this.thread.background });
74
- if (await this.popConvStep()) {
75
- await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'onAwake' });
76
- }
77
- this.end();
59
+ // if (await this.popConvStep()) {
60
+ // await this.sendEventToStep({ toWorker: this.isWorker, action: 'onAwake' })
61
+ // }
62
+ await this.sendCommands(call, [{ name: 'grammar', params: {} }]);
63
+ if (!this.thread.ending)
64
+ this.end();
78
65
  });
79
66
  // TODO remove this hack that prevents this.triggers.once execution below
80
67
  // currently it skips logic to override triggers (is there better way to do it?)
81
- if (this.event.name.startsWith('@end'))
68
+ if (this.event.name.startsWith('@'))
82
69
  return;
83
70
  }
84
71
  this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
@@ -86,7 +73,7 @@ class GlobalCommand extends voice_1.default {
86
73
  switch (event.params.type) {
87
74
  case 'hangup': {
88
75
  await this.hangup(call);
89
- await this.popConvStep();
76
+ // await this.popConvStep()
90
77
  await this.notifyConvEnd();
91
78
  return this.end();
92
79
  }
@@ -137,10 +124,10 @@ class GlobalCommand extends voice_1.default {
137
124
  return {};
138
125
  }
139
126
  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
- }
127
+ // const localHandler = await this.sendEventToStep({ toWorker: false, event: { ...this.event, processed: undefined, action: 'lcl' } })
128
+ // if (!localHandler) {
129
+ // this.throwError(event.params.error)
130
+ // }
144
131
  this.event.processed = true;
145
132
  break;
146
133
  }
@@ -223,7 +210,7 @@ class GlobalCommand extends voice_1.default {
223
210
  async onAwake() {
224
211
  await super.onAwake();
225
212
  await this.initGrammar();
226
- await this.globThread();
213
+ await this.worker();
227
214
  this.triggers.refreshAll();
228
215
  }
229
216
  exitToThread() {
@@ -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() {
@@ -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 = {
@@ -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,18 +33,18 @@ 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
49
  this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
50
50
  // if (channel.callback.id)
@@ -53,11 +53,11 @@ class SendDTMF extends voice_1.default {
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/step.d.ts CHANGED
@@ -1,36 +1,49 @@
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[];
7
+ export interface IConversation {
10
8
  lcl: IConversationStep[];
11
- sid?: ISessionId;
9
+ glb: IConversationStep[];
12
10
  }
13
- interface IConversationData {
11
+ export interface IConversationData {
14
12
  _conv: IConversation;
15
13
  }
16
- export default class ConvStep<TData = unknown, TIn = unknown, TOut = unknown, TParams = any> extends Step<TIn & ConvStepDataIn, TOut, TParams> {
14
+ export declare const enum ConvThreadType {
15
+ lcl = "lcl",
16
+ glb = "glb",
17
+ wrk = "wrk"
18
+ }
19
+ interface IConversationStep {
20
+ key: string;
21
+ trd: string;
22
+ typ?: string;
23
+ name?: string;
24
+ label?: string;
25
+ step?: string;
26
+ }
27
+ export default abstract class ConvStep<TData extends IConversationData, TIn = unknown, TOut = unknown, TParams = any> extends Step<TIn & ConvStepDataIn, TOut, TParams> {
17
28
  private convDataCache?;
18
- get cache(): TData & IConversationData | undefined;
29
+ abstract _worker(data: IConversationData): Promise<void>;
30
+ get cache(): TData | undefined;
19
31
  get conversation(): string | IMergeField;
20
32
  get conversationId(): string;
21
- get globalThreadId(): string;
33
+ get workerThreadId(): string;
22
34
  /** id of the thread where conversation data is stored */
23
35
  get dataThreadId(): IThreadId;
24
36
  get isGlobal(): boolean;
37
+ get threadType(): ConvThreadType;
38
+ get isWorker(): boolean;
25
39
  get useQueue(): boolean;
26
- fetchData(): Promise<TData & IConversationData>;
40
+ fetchData(): Promise<TData>;
27
41
  getConversation(): Promise<IConversation>;
28
42
  updateData(): Promise<void>;
29
43
  hasConversation(): Promise<boolean>;
44
+ protected worker(): Promise<void>;
30
45
  runBefore(): Promise<void>;
31
- runHandle(): Promise<unknown>;
32
46
  runAfter(): Promise<void>;
33
- hasActiveGlobal(): Promise<boolean>;
34
47
  waitForConversation(): Promise<void>;
35
48
  protected onSkipEvent(): Promise<void>;
36
49
  cancel(): Promise<void>;
@@ -47,21 +60,12 @@ export default class ConvStep<TData = unknown, TIn = unknown, TOut = unknown, TP
47
60
  onConvEnd(): Promise<void>;
48
61
  pause(): Promise<void>;
49
62
  resume(): Promise<void>;
50
- protected sendEventToStep({ toGlobal, toStep, action, event }: {
51
- action?: string;
52
- event?: IEvent;
53
- toStep?: IConversationStep;
54
- toGlobal?: boolean;
55
- }): Promise<IStepResult | undefined>;
56
- activeStep(isGlobal?: boolean): Promise<IConversationStep | undefined>;
57
63
  /** @returns true if current conv is active */
58
- protected pushConvStep(): Promise<boolean>;
59
64
  /** @returns true if current conv was active */
60
- protected popConvStep(popStep?: IConversationStep): Promise<boolean>;
61
65
  startConversation(data: TData, { thread }?: {
62
66
  thread?: IThreadId;
63
67
  }): Promise<void>;
64
- protected _fetchCache(): Promise<(TData & IConversationData) | undefined>;
68
+ protected _fetchCache(): Promise<(TData) | undefined>;
65
69
  protected _clearCache(): void;
66
70
  protected _refreshCache(): Promise<void>;
67
71
  }
package/dst/step.js CHANGED
@@ -25,7 +25,7 @@ class ConvStep extends step_1.default {
25
25
  return convOrName;
26
26
  throw new Error(`invalid conversation ${JSON.stringify(conv)}`);
27
27
  }
28
- get globalThreadId() {
28
+ get workerThreadId() {
29
29
  return `G:${this.conversationId}`;
30
30
  }
31
31
  /** id of the thread where conversation data is stored */
@@ -36,6 +36,14 @@ class ConvStep extends step_1.default {
36
36
  get isGlobal() {
37
37
  return false;
38
38
  }
39
+ get threadType() {
40
+ return this.thread.id === this.workerThreadId
41
+ ? (this.isGlobal ? "glb" /* ConvThreadType.glb */ : "wrk" /* ConvThreadType.wrk */)
42
+ : "lcl" /* ConvThreadType.lcl */;
43
+ }
44
+ get isWorker() {
45
+ return this.threadType !== "lcl" /* ConvThreadType.lcl */;
46
+ }
39
47
  get useQueue() {
40
48
  return true;
41
49
  }
@@ -58,13 +66,18 @@ class ConvStep extends step_1.default {
58
66
  async hasConversation() {
59
67
  return (await this._fetchCache())?._conv != null;
60
68
  }
69
+ async worker() {
70
+ this.triggers.local('_conv');
71
+ if (this.cache == null)
72
+ return;
73
+ await this._worker(this.cache);
74
+ }
61
75
  async runBefore() {
62
76
  await super.runBefore();
63
77
  if (this.thread.ending)
64
78
  return;
65
79
  // ensure cache is reset between step runs, just in case
66
80
  this._clearCache();
67
- this.triggers.local('_conv');
68
81
  if (!await this.hasConversation())
69
82
  return;
70
83
  this.log.debug('conv.runBefore', { state: this.state, conv: this.convDataCache?._conv });
@@ -76,36 +89,69 @@ class ConvStep extends step_1.default {
76
89
  this.state.name = this.event.action;
77
90
  return;
78
91
  }
79
- if (this.useQueue && !await this.pushConvStep()) {
80
- return await this.waitForConversation();
81
- }
82
- }
83
- async runHandle() {
84
- const sid = this.cache?._conv.sid;
85
- if (sid != null && sid !== this.session.sessionId) {
86
- this.triggers.refreshAll();
87
- }
88
- return await super.runHandle();
89
- }
90
- async runAfter() {
91
- if (this.thread.ending)
92
+ console.log('BEFORE', {
93
+ event: this.event,
94
+ state: this.state,
95
+ name: this.constructor.name,
96
+ thread: this.thread.id,
97
+ ...this.cache?._conv
98
+ });
99
+ // if (this.useQueue && !await this.pushConvStep()) {
100
+ // return await this.waitForConversation()
101
+ // }
102
+ if (this.cache == null)
92
103
  return;
93
- if (!await this.hasConversation())
94
- return await super.runAfter();
95
- if (this.thread.hasControlAction()) {
96
- // await this.popConvStep()
97
- if (await this.popConvStep()) {
98
- await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'onAwake' });
104
+ // const glb = this.cache._conv.glb
105
+ // if (!this.isGlobal && glb.length > 0 && glb[glb.length - 1].typ !== ConvThreadType.wrk) return
106
+ if (!this.isGlobal)
107
+ return;
108
+ if (this.thread.id !== this.workerThreadId) {
109
+ const worker = this.process.newThread(this.workerThreadId, thread => {
110
+ thread.state = {
111
+ name: 'worker',
112
+ step: this.step.id,
113
+ thread: this.dataThreadId
114
+ };
115
+ });
116
+ if (worker.isNewThread === true || worker.state.step !== this.step.id) {
117
+ worker.state.step = this.step.id;
118
+ worker.activate();
119
+ await worker.run();
120
+ await this._refreshCache();
99
121
  }
100
122
  }
101
- if (this.isGlobal && !this.event.processed) {
102
- await this.sendEventToStep({ toGlobal: false, event: { ...this.event, processed: undefined, action: 'local' } });
103
- }
104
- await super.runAfter();
105
123
  }
106
- async hasActiveGlobal() {
107
- const globalLength = (await this.getConversation())?.glb?.length ?? 0;
108
- return globalLength > 0;
124
+ // public async runHandle (): Promise<unknown> {
125
+ // if (this.thread.ending || this.state.name === 'onConvEnd') {
126
+ // return await super.runHandle()
127
+ // }
128
+ // // const sid = this.cache?._conv.sid
129
+ // // if (sid != null && sid !== this.session.sessionId) {
130
+ // // this.triggers.refreshAll()
131
+ // // }
132
+ // return await super.runHandle()
133
+ // }
134
+ async runAfter() {
135
+ console.log('AFTER', {
136
+ name: this.constructor.name,
137
+ thread: this.thread.id,
138
+ ...this.cache?._conv
139
+ });
140
+ // if (this.thread.ending || this.state.name === 'onConvEnd') {
141
+ // return await super.runAfter()
142
+ // }
143
+ // if (!await this.hasConversation()) return await super.runAfter()
144
+ // if (this.thread.hasControlAction()) {
145
+ // // await this.popConvStep()
146
+ // if (await this.popConvStep()) {
147
+ // // TODO: this seems not to be necessary now or not?
148
+ // await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'onAwake' })
149
+ // }
150
+ // }
151
+ // if (this.isGlobal && !this.event.processed) {
152
+ // await this.sendEventToStep({ toGlobal: false, event: { ...this.event, processed: undefined, action: 'lcl' } })
153
+ // }
154
+ await super.runAfter();
109
155
  }
110
156
  // public async setLocalTriggers (): Promise<void> {
111
157
  // if (!this.isGlobal && await this.hasActiveGlobal()) {
@@ -132,15 +178,15 @@ class ConvStep extends step_1.default {
132
178
  delete this.state.prevSkipName;
133
179
  }
134
180
  async cancel() {
135
- if (this.isGlobal) {
181
+ if (this.isWorker) {
136
182
  this.log.debug('conv.cannot cancel global');
137
- return;
183
+ // return
138
184
  }
139
- const activeStep = await this.activeStep();
140
- await this.sendEventToStep({ event: { name: '_conv', action: 'onCancel', params: { byStep: this.step.label } }, toStep: activeStep });
185
+ // const activeStep = await this.activeStep(this.isWorker)
186
+ // await this.sendEventToStep({ event: { name: '_conv', action: 'onCancel', params: { byStep: this.step.label } }, toStep: activeStep })
141
187
  }
142
188
  async onCancel() {
143
- await this.popConvStep();
189
+ // await this.popConvStep()
144
190
  if (this.getExitStepId('cancel')) {
145
191
  this.log.debug('conv.cancel exit');
146
192
  this.exitStep('cancel');
@@ -156,7 +202,7 @@ class ConvStep extends step_1.default {
156
202
  this.state.name = this.state.resumeState;
157
203
  this.state.resumeState = undefined;
158
204
  const conv = await this.getConversation();
159
- if (this.isGlobal && conv.glb.length === 1) {
205
+ if (this.isWorker && conv.glb.length === 1) {
160
206
  this.log.debug('make global thread foreground');
161
207
  this.thread.background = false;
162
208
  }
@@ -172,20 +218,21 @@ class ConvStep extends step_1.default {
172
218
  async notifyConvEnd({ onlyLocal } = {}) {
173
219
  const conv = await this.getConversation();
174
220
  const lcl = conv.lcl.splice(0);
175
- const glb = onlyLocal ? [] : conv.glb.splice(0);
176
- if (lcl.length > 0 || glb.length > 0) {
221
+ const wrk = onlyLocal ? [] : conv.glb.splice(0);
222
+ if (lcl.length > 0 || wrk.length > 0) {
177
223
  await this.updateData();
178
- for (const step of lcl) {
179
- await this.sendEventToStep({ toStep: step, action: 'onConvEnd' });
180
- }
181
- for (const step of glb) {
182
- await this.sendEventToStep({ toStep: step, action: 'onConvEnd' });
183
- }
224
+ // for (const step of lcl) {
225
+ // await this.sendEventToStep({ toStep: step, action: 'onConvEnd' })
226
+ // }
227
+ // for (const step of wrk) {
228
+ // await this.sendEventToStep({ toStep: step, action: 'onConvEnd' })
229
+ // }
184
230
  }
185
231
  }
186
232
  async waitConvEnd() {
187
- const conversationThread = this.state.thread ?? this.thread.id;
188
- if (!this.isGlobal && conversationThread === this.thread.id && this.thread.id !== 'main') {
233
+ const workerThread = this.process.getThread(this.workerThreadId);
234
+ const workerData = await workerThread?.currentStep.resolveDataIn();
235
+ if (!this.isGlobal && this.thread.id !== 'main' && this.thread.id === workerData?.conversationThread) {
189
236
  this.log.debug('conv.wait for end', this.conversation);
190
237
  this.gotoState({ ...this.state, name: 'waitGlobEnd', direct: true });
191
238
  }
@@ -195,7 +242,7 @@ class ConvStep extends step_1.default {
195
242
  }
196
243
  }
197
244
  async waitGlobEnd() {
198
- const gcThreadId = this.globalThreadId;
245
+ const gcThreadId = this.workerThreadId;
199
246
  // wait for glob thread end (if exists)
200
247
  this.triggers.hook({ name: 'end', thread: gcThreadId }, async () => await this.onConvEnd());
201
248
  if (!this.process.getThread(gcThreadId))
@@ -218,7 +265,7 @@ class ConvStep extends step_1.default {
218
265
  }
219
266
  async resume() {
220
267
  // const conv = await this._getConversation()
221
- // if (conv.stk.length > 0) {
268
+ // if (conv.stk.length> 0) {
222
269
  // conv.lcl.unshift(...(conv.stk.pop() ?? [])) // restore que from stk
223
270
  // await this.updateData()
224
271
  // await this.sendEventToStep({ toGlobal: false, action: 'onResume' })
@@ -226,66 +273,96 @@ class ConvStep extends step_1.default {
226
273
  // this.log.debug('conv.nothing to resume')
227
274
  // }
228
275
  }
229
- async sendEventToStep({ toGlobal, toStep, action, event = { name: '_conv', action } }) {
230
- toStep || (toStep = await this.activeStep(toGlobal));
231
- if (toStep != null) {
232
- const target = { type: 'session', name: toStep.key, id: toStep.thread };
233
- return await this.thread.eventManager.emit(event, { invocationType: 'async', target });
234
- }
235
- return undefined;
236
- }
237
- async activeStep(isGlobal = this.isGlobal) {
238
- const conv = await this.getConversation();
239
- return isGlobal ? conv.glb[conv.glb.length - 1] : conv.lcl[conv.lcl.length - 1];
240
- }
276
+ // protected async sendEventToStep ({
277
+ // toGlobal = false,
278
+ // toStep,
279
+ // action,
280
+ // event = { name: '_conv', action }
281
+ // }: {
282
+ // action?: string
283
+ // event?: IEvent
284
+ // toStep?: IConversationStep
285
+ // toGlobal?: boolean
286
+ // }): Promise<IStepResult | undefined> {
287
+ // toStep ||= await this.activeStep(toGlobal)
288
+ // if (toStep != null) {
289
+ // const target = { type: 'session' as any, name: toStep.key, id: toStep.trd }
290
+ // return await this.thread.eventManager.emit(
291
+ // event,
292
+ // { invocationType: 'async', target }
293
+ // )
294
+ // }
295
+ // return undefined
296
+ // }
297
+ // async activeStep (toGlobal: boolean): Promise<IConversationStep | undefined> {
298
+ // const conv = await this.getConversation()
299
+ // const que = toGlobal ? conv.glb : conv.lcl
300
+ // if (que == null) return
301
+ // const lastItem = que[que.length - 1]
302
+ // if (toGlobal && lastItem?.typ === ConvThreadType.wrk) return undefined
303
+ // return que[que.length - 1]
304
+ // }
241
305
  /** @returns true if current conv is active */
242
- async pushConvStep() {
243
- const conv = await this.getConversation();
244
- const sessionKey = this.session.key;
245
- if (sessionKey == null)
246
- throw new Error('session is required');
247
- const que = this.isGlobal ? conv.glb : conv.lcl;
248
- const queIndex = que.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread);
249
- if (queIndex < 0) {
250
- await this.sendEventToStep({ toGlobal: this.isGlobal, event: { name: '_conv', action: 'onPause', params: { byStep: this.step.label } } });
251
- que.push({ key: sessionKey, thread: this.thread.id, name: this.constructor.name, label: this.label, step: this.id });
252
- await this.updateData();
253
- return true;
254
- }
255
- return queIndex === que.length - 1;
256
- }
306
+ // protected async pushConvStep (): Promise<boolean> {
307
+ // const conv = await this.getConversation()
308
+ // const sessionKey = this.session.key
309
+ // if (sessionKey == null) throw new Error('session is required')
310
+ // const que = this.isWorker ? conv.glb : conv.lcl
311
+ // const lastItem = que[que.length - 1]
312
+ // const lastItemType = lastItem?.typ
313
+ // if (lastItemType === ConvThreadType.wrk) {
314
+ // console.log('POP', lastItem)
315
+ // que.pop() // remove last item, since it's temporary
316
+ // // TODO triggers.refreshAll!
317
+ // }
318
+ // const queIndex = que.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.trd)
319
+ // if (queIndex < 0) {
320
+ // if (que.length > 0) {
321
+ // await this.sendEventToStep({ toGlobal: this.isGlobal, event: { name: '_conv', action: 'onPause', params: { byStep: this.step.label } } })
322
+ // }
323
+ // const type = this.threadType === ConvThreadType.wrk ? this.threadType : undefined
324
+ // que.push({ key: sessionKey, trd: this.thread.id, typ: type, name: this.constructor.name, label: this.label, step: this.id })
325
+ // console.log('PUSH', que[que.length - 1])
326
+ // await this.updateData()
327
+ // return true
328
+ // }
329
+ // // refresh data on step change
330
+ // que[queIndex].name = this.constructor.name
331
+ // que[queIndex].label = this.label
332
+ // que[queIndex].step = this.id
333
+ // return queIndex === que.length - 1
334
+ // }
257
335
  /** @returns true if current conv was active */
258
- async popConvStep(popStep) {
259
- const conv = await this.getConversation();
260
- const sessionKey = this.session.key;
261
- if (sessionKey == null)
262
- throw new Error('session is required');
263
- popStep || (popStep = { key: sessionKey, thread: this.thread.id });
264
- const que = this.isGlobal ? conv.glb : conv.lcl;
265
- const queIndex = que.findIndex(stp => stp.key === popStep?.key && stp.thread === popStep.thread);
266
- if (queIndex >= 0) {
267
- // console.log('SPLICE1', this.thread.id, this.step.label, que)
268
- que.splice(queIndex, 1);
269
- conv.sid = this.session.sessionId;
270
- await this.updateData();
271
- return queIndex === que.length;
272
- }
273
- // let updated = false
274
- // conv.stk.forEach((steps, stepsIdx) => {
275
- // const stpIdx = steps.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread)
276
- // if (stpIdx >= 0) {
277
- // if (steps.length > 1) steps.splice(stpIdx, 1)
278
- // else conv.stk.splice(stepsIdx, 1)
279
- // updated = true
280
- // }
281
- // })
282
- // if (updated) await this.updateData()
283
- return false;
284
- }
336
+ // protected async popConvStep (popStep?: IConversationStep): Promise<boolean> {
337
+ // const conv = await this.getConversation()
338
+ // const sessionKey = this.session.key
339
+ // if (sessionKey == null) throw new Error('session is required')
340
+ // popStep ||= { key: sessionKey, trd: this.thread.id }
341
+ // const que = this.isWorker ? conv.glb : conv.lcl
342
+ // const queIndex = que.findIndex(stp => stp.key === popStep?.key && stp.trd === popStep.trd)
343
+ // if (queIndex >= 0) {
344
+ // // console.log('SPLICE1', this.thread.id, this.step.label, que)
345
+ // que.splice(queIndex, 1)
346
+ // // conv.sid = this.session.sessionId
347
+ // await this.updateData()
348
+ // return queIndex === que.length
349
+ // }
350
+ // // let updated = false
351
+ // // conv.stk.forEach((steps, stepsIdx) => {
352
+ // // const stpIdx = steps.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread)
353
+ // // if (stpIdx>= 0) {
354
+ // // if (steps.length> 1) steps.splice(stpIdx, 1)
355
+ // // else conv.stk.splice(stepsIdx, 1)
356
+ // // updated = true
357
+ // // }
358
+ // // })
359
+ // // if (updated) await this.updateData()
360
+ // return false
361
+ // }
285
362
  async startConversation(data, { thread: _thread } = {}) {
286
363
  this.convDataCache = {
287
364
  ...data,
288
- _conv: {
365
+ _conv: data._conv ?? {
289
366
  glb: [],
290
367
  lcl: []
291
368
  }
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;
@@ -44,6 +44,7 @@ export interface CallStartEvent extends VoiceEvent {
44
44
  export default class VoiceStep<TIn = unknown, TOut = unknown, TParams extends VoiceEvent = VoiceEvent> extends ConvStep<IVoiceCall, TIn & {
45
45
  handleCancel?: boolean;
46
46
  }, TOut, TParams> {
47
+ _worker(call: IVoiceCall): Promise<void>;
47
48
  runBefore(): Promise<void>;
48
49
  sendCommands({ id, type, callback }: IVoiceCall, commands: TODO[]): Promise<void>;
49
50
  handleHeartbeat(call: IVoiceCall): Promise<void>;
package/dst/voice.js CHANGED
@@ -13,17 +13,27 @@ class VoiceStepError extends base_1.default {
13
13
  exports.VoiceStepError = VoiceStepError;
14
14
  class VoiceStep extends step_1.default {
15
15
  // static Error = VoiceStepError
16
+ async _worker(call) {
17
+ this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
18
+ console.log('EVENT', {
19
+ name: this.constructor.name,
20
+ thread: this.thread.id,
21
+ event
22
+ });
23
+ });
24
+ }
16
25
  async runBefore() {
17
26
  await super.runBefore();
18
27
  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
- }
28
+ // if (!this.event.processed && this.event.action == null) {
29
+ // const toWorker = this.event.params.global
30
+ // const hasHandler = await this.sendEventToStep({ event: { ...this.event, action: toWorker ? 'wrk' : 'lcl' }, toWorker }) // redirect to global (1)
31
+ // if (hasHandler != null) {
32
+ // this.log.debug('hasWorker', { hasGlobal: hasHandler })
33
+ // this.state.prevSkipName = this.state.name
34
+ // this.state.name = 'onSkipEvent'
35
+ // }
36
+ // }
27
37
  await this.handleHeartbeat(this.cache);
28
38
  }
29
39
  }
@@ -35,10 +45,11 @@ class VoiceStep extends step_1.default {
35
45
  name: `out/voice/${type}`,
36
46
  params: {
37
47
  id,
38
- cancel: this.isGlobal ? undefined : true,
39
- async: this.isGlobal ? true : undefined,
48
+ cancel: this.isWorker ? undefined : true,
49
+ async: this.isWorker ? true : undefined,
40
50
  hbs: 99,
41
- commands
51
+ commands,
52
+ step: { key: this.session.key, trd: this.isGlobal ? this.workerThreadId : this.thread.id } // response should be sent to this session
42
53
  },
43
54
  reporting: this.session.getSessionRef()
44
55
  }, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onereach/step-voice",
3
- "version": "5.0.9-fixsubflow.1",
3
+ "version": "5.0.9-fixsubflow.3",
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.12",
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",