@onereach/step-voice 4.0.13 → 4.0.16
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/Global Command.d.ts +1 -1
- package/dst/Global Command.js +1 -0
- package/dst/Initiate Call.d.ts +23 -4
- package/dst/Initiate Call.js +22 -17
- package/dst/Say Message.d.ts +1 -1
- package/dst/Say Message.js +2 -1
- package/dst/Wait For Call.d.ts +5 -5
- package/dst/Wait For Call.js +11 -8
- package/dst/step.d.ts +49 -0
- package/dst/step.js +264 -0
- package/dst/voice.d.ts +9 -4
- package/dst/voice.js +2 -12
- package/package.json +4 -4
package/dst/Global Command.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ interface EVENT extends VoiceEvent {
|
|
|
25
25
|
tags?: string[];
|
|
26
26
|
out?: string;
|
|
27
27
|
}
|
|
28
|
-
export default class GlobalCommand extends VoiceStep<INPUT
|
|
28
|
+
export default class GlobalCommand extends VoiceStep<Partial<INPUT>, OUTPUT, EVENT> {
|
|
29
29
|
get isGlobal(): boolean;
|
|
30
30
|
get autoCancel(): boolean | undefined;
|
|
31
31
|
runStep(): Promise<void>;
|
package/dst/Global Command.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions, @typescript-eslint/explicit-function-return-type */
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
const tslib_1 = require("tslib");
|
|
4
5
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
package/dst/Initiate Call.d.ts
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import VoiceStep, { CallStartEvent, TODO } from './voice';
|
|
2
|
+
interface INPUT {
|
|
3
|
+
channelId?: string;
|
|
4
|
+
sessionTimeout?: number;
|
|
5
|
+
asr: TODO;
|
|
6
|
+
tts: TODO;
|
|
7
|
+
from: string;
|
|
8
|
+
endUserNumber: string;
|
|
9
|
+
sipHost?: string;
|
|
10
|
+
sipUser?: string;
|
|
11
|
+
sipPassword?: string;
|
|
12
|
+
timeout?: number;
|
|
13
|
+
headers?: Array<{
|
|
14
|
+
name: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}>;
|
|
17
|
+
enableSpoofCallerId?: boolean;
|
|
18
|
+
spoofCallerId?: string;
|
|
19
|
+
isAMD?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export default class InitiateCall extends VoiceStep<INPUT, TODO, CallStartEvent> {
|
|
22
|
+
get conversation(): string | import("@onereach/flow-sdk/dst/types").IMergeField;
|
|
4
23
|
get autoCancel(): undefined;
|
|
5
24
|
runStep(): Promise<void>;
|
|
6
25
|
waitForCall(): Promise<void>;
|
|
7
26
|
}
|
|
8
|
-
|
|
27
|
+
export {};
|
package/dst/Initiate Call.js
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions, @typescript-eslint/explicit-function-return-type */
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
const tslib_1 = require("tslib");
|
|
4
5
|
const voice_1 = tslib_1.__importDefault(require("./voice"));
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
7
|
+
const timestring_1 = tslib_1.__importDefault(require("timestring"));
|
|
8
|
+
const uuid_1 = tslib_1.__importDefault(require("uuid"));
|
|
9
|
+
const defaultSessionTimeoutMin = 5;
|
|
9
10
|
class InitiateCall extends voice_1.default {
|
|
10
11
|
get conversation() {
|
|
11
|
-
|
|
12
|
+
if (this.step.dataOut == null)
|
|
13
|
+
throw new Error('missing conversation');
|
|
14
|
+
return this.step.dataOut;
|
|
12
15
|
}
|
|
13
16
|
get autoCancel() {
|
|
14
17
|
return undefined;
|
|
15
18
|
}
|
|
16
19
|
async runStep() {
|
|
17
|
-
const { channelId =
|
|
20
|
+
const { channelId = uuid_1.default.v4() } = this.data;
|
|
18
21
|
if (!this.session.key) {
|
|
19
|
-
const sessionTimeoutMs =
|
|
22
|
+
const sessionTimeoutMs = (0, timestring_1.default)(`${this.data.sessionTimeout ?? defaultSessionTimeoutMin} min`, 'ms');
|
|
20
23
|
await this.session.start({
|
|
21
24
|
key: channelId,
|
|
22
25
|
timeout: sessionTimeoutMs,
|
|
@@ -27,7 +30,8 @@ class InitiateCall extends voice_1.default {
|
|
|
27
30
|
}
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
|
-
|
|
33
|
+
const convData = { id: channelId, type: 'voicer' };
|
|
34
|
+
await this.startConversation(convData, { events: {} });
|
|
31
35
|
this.gotoState('waitForCall');
|
|
32
36
|
}
|
|
33
37
|
async waitForCall() {
|
|
@@ -44,18 +48,18 @@ class InitiateCall extends voice_1.default {
|
|
|
44
48
|
}
|
|
45
49
|
};
|
|
46
50
|
});
|
|
47
|
-
await this.startConversation(event.params.channel, { events:
|
|
51
|
+
await this.startConversation(event.params.channel, { events: {} });
|
|
48
52
|
this.log.warn({ channel: event.params.channel, conv: this.conversation, data: this.get(this.conversation) }, 'Confirming that the OB call can be started');
|
|
49
53
|
return this.exitFlow();
|
|
50
54
|
}
|
|
51
55
|
case 'call': {
|
|
52
56
|
const channel = {
|
|
53
|
-
botNumber: event.params.channel.to,
|
|
54
|
-
endUserNumber,
|
|
55
57
|
headers: event.params.headers,
|
|
58
|
+
...event.params.channel,
|
|
56
59
|
asr: asr.getSettings(),
|
|
57
60
|
tts: tts.getVoice(),
|
|
58
|
-
|
|
61
|
+
botNumber: event.params.channel.to ?? 'unknown',
|
|
62
|
+
endUserNumber
|
|
59
63
|
};
|
|
60
64
|
delete channel.from;
|
|
61
65
|
delete channel.to;
|
|
@@ -88,7 +92,7 @@ class InitiateCall extends voice_1.default {
|
|
|
88
92
|
return this.exitStep('success');
|
|
89
93
|
}
|
|
90
94
|
case 'error': {
|
|
91
|
-
const error =
|
|
95
|
+
const error = lodash_1.default.get(event, 'params.error.originateStatus');
|
|
92
96
|
const errorChannel = {
|
|
93
97
|
botNumber,
|
|
94
98
|
endUserNumber
|
|
@@ -108,9 +112,10 @@ class InitiateCall extends voice_1.default {
|
|
|
108
112
|
return this.exitStep('no answer', errorChannel);
|
|
109
113
|
}
|
|
110
114
|
await this.transcript(errorChannel, { action: 'Call Error' });
|
|
115
|
+
const errorParams = event.params.error;
|
|
111
116
|
return this.throwError({
|
|
112
|
-
name: event.params.error
|
|
113
|
-
message: `${
|
|
117
|
+
name: event.params.error?.name ?? 'VoiceError',
|
|
118
|
+
message: `${errorParams.date}: ${errorParams.originateStatus}`
|
|
114
119
|
});
|
|
115
120
|
}
|
|
116
121
|
default:
|
|
@@ -119,8 +124,8 @@ class InitiateCall extends voice_1.default {
|
|
|
119
124
|
});
|
|
120
125
|
this.triggers.otherwise(async () => {
|
|
121
126
|
await this.triggers.flush();
|
|
122
|
-
const originateTimeout =
|
|
123
|
-
const customHeaders =
|
|
127
|
+
const originateTimeout = (0, timestring_1.default)(`${timeout ?? 30} sec`, 'ms'); // timeout or 30 seconds
|
|
128
|
+
const customHeaders = lodash_1.default.reduce(headers, (memo, header) => {
|
|
124
129
|
memo[header.name] = `${header.value}`;
|
|
125
130
|
return memo;
|
|
126
131
|
}, {});
|
package/dst/Say Message.d.ts
CHANGED
package/dst/Say Message.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions, @typescript-eslint/explicit-function-return-type */
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
const tslib_1 = require("tslib");
|
|
4
5
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
5
6
|
const voice_1 = tslib_1.__importDefault(require("./voice"));
|
|
6
7
|
class SayMessage extends voice_1.default {
|
|
7
8
|
async runStep() {
|
|
8
|
-
const { audio, textType, tts, sensitiveData, interSpeechTimeout } = this.data;
|
|
9
|
+
const { audio, textType, tts, sensitiveData, interSpeechTimeout = 0 } = this.data;
|
|
9
10
|
const channel = await this.fetchData();
|
|
10
11
|
this.triggers.once(`in/voice/${channel.id}/event`, async (event) => {
|
|
11
12
|
if (channel.recordCall && sensitiveData?.muteStep) {
|
package/dst/Wait For Call.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import VoiceStep, { CallStartEvent, TODO } from './voice';
|
|
2
|
+
import { ITypedEvent } from '@onereach/flow-sdk/dst/types';
|
|
3
|
+
export default class WaitForCall extends VoiceStep<TODO, TODO, TODO> {
|
|
4
|
+
get conversation(): string | import("@onereach/flow-sdk/dst/types").IMergeField;
|
|
4
5
|
get autoCancel(): boolean;
|
|
5
6
|
runStep(): Promise<void>;
|
|
6
|
-
onCall(event:
|
|
7
|
+
onCall(event: ITypedEvent<CallStartEvent>): Promise<unknown>;
|
|
7
8
|
}
|
|
8
|
-
import VoiceStep from "./voice";
|
package/dst/Wait For Call.js
CHANGED
|
@@ -1,32 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions, @typescript-eslint/explicit-function-return-type */
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
const tslib_1 = require("tslib");
|
|
4
5
|
const voice_1 = tslib_1.__importDefault(require("./voice"));
|
|
5
|
-
const
|
|
6
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
6
7
|
const defaultSessionTimeout = 5 * 60 * 1000;
|
|
7
8
|
class WaitForCall extends voice_1.default {
|
|
8
9
|
get conversation() {
|
|
9
|
-
|
|
10
|
+
if (this.step.dataOut == null)
|
|
11
|
+
throw new Error('missing conversation');
|
|
12
|
+
return this.step.dataOut;
|
|
10
13
|
}
|
|
11
14
|
get autoCancel() {
|
|
12
15
|
return false;
|
|
13
16
|
}
|
|
14
17
|
async runStep() {
|
|
15
18
|
const selectedNumbers = this.data.selectedNumbers;
|
|
16
|
-
|
|
17
|
-
this.triggers.on(`in/voice/${number.value}/call`, event => this.onCall(event));
|
|
19
|
+
lodash_1.default.forEach(selectedNumbers, number => {
|
|
20
|
+
this.triggers.on(`in/voice/${number.value}/call`, async (event) => await this.onCall(event));
|
|
18
21
|
});
|
|
19
22
|
}
|
|
20
23
|
async onCall(event) {
|
|
21
24
|
const { autoHangup, asr, tts, endOfInputTimeout } = this.data;
|
|
22
25
|
const channel = {
|
|
23
|
-
autoHangup,
|
|
24
|
-
botNumber: event.params.channel.to,
|
|
25
|
-
endUserNumber: event.params.channel.from,
|
|
26
26
|
headers: event.params.headers,
|
|
27
|
+
...event.params.channel,
|
|
28
|
+
autoHangup,
|
|
27
29
|
asr: asr.getSettings(),
|
|
28
30
|
tts: tts.getVoice(),
|
|
29
|
-
|
|
31
|
+
botNumber: event.params.channel.to ?? 'unknown',
|
|
32
|
+
endUserNumber: event.params.channel?.from ?? 'unknown'
|
|
30
33
|
};
|
|
31
34
|
delete channel.from;
|
|
32
35
|
delete channel.to;
|
package/dst/step.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Step from '@onereach/flow-sdk/dst/step';
|
|
2
|
+
import { IThreadId, IEvent, IStepResult, IMergeField, IMergeFieldKey } from '@onereach/flow-sdk/dst/types';
|
|
3
|
+
import { IConversationData, IConversation, IConversationStep } from '@onereach/step-conversation/dst/types';
|
|
4
|
+
export interface ConvStepDataIn {
|
|
5
|
+
conversation?: IMergeFieldKey | IMergeField;
|
|
6
|
+
conversationThread?: string;
|
|
7
|
+
}
|
|
8
|
+
export default class ConvStep<TData = unknown, TIn = unknown, TOut = unknown, TParams = any> extends Step<TIn & ConvStepDataIn, TOut, TParams> {
|
|
9
|
+
convDataCache?: TData & IConversationData;
|
|
10
|
+
get conversation(): IMergeFieldKey | IMergeField;
|
|
11
|
+
/** id of the thread where conversation data is stored */
|
|
12
|
+
get dataThreadId(): IThreadId;
|
|
13
|
+
get autoCancel(): boolean | undefined;
|
|
14
|
+
get isGlobal(): boolean;
|
|
15
|
+
get useQueue(): boolean;
|
|
16
|
+
fetchData(): Promise<TData & IConversationData>;
|
|
17
|
+
updateData(): Promise<void>;
|
|
18
|
+
hasConversation(): Promise<boolean>;
|
|
19
|
+
runBefore(): Promise<void>;
|
|
20
|
+
runAfter(): Promise<void>;
|
|
21
|
+
hasActiveGlobal(): Promise<boolean>;
|
|
22
|
+
setTriggerType(): Promise<void>;
|
|
23
|
+
waitForConversation(): Promise<void>;
|
|
24
|
+
onSkipEvent(): Promise<void>;
|
|
25
|
+
cancel(): Promise<void>;
|
|
26
|
+
onCancel(): Promise<void>;
|
|
27
|
+
waitCancelEnd(): Promise<void>;
|
|
28
|
+
pause(): Promise<void>;
|
|
29
|
+
resume(): Promise<void>;
|
|
30
|
+
sendEventToStep({ toGlobal, toStep, action, event }: {
|
|
31
|
+
action?: string;
|
|
32
|
+
event?: IEvent;
|
|
33
|
+
toStep?: IConversationStep;
|
|
34
|
+
toGlobal?: boolean;
|
|
35
|
+
}): Promise<IStepResult | undefined>;
|
|
36
|
+
activeStep(isGlobal?: boolean): Promise<IConversationStep | undefined>;
|
|
37
|
+
/** @returns true if current conv is active */
|
|
38
|
+
pushConvStep(): Promise<boolean>;
|
|
39
|
+
/** @returns true if current conv was active */
|
|
40
|
+
popConvStep(): Promise<boolean>;
|
|
41
|
+
startConversation(data: TData, { autoCancel, events }: {
|
|
42
|
+
autoCancel?: boolean;
|
|
43
|
+
events: Record<string, {}>;
|
|
44
|
+
thread?: IThreadId;
|
|
45
|
+
}): Promise<void>;
|
|
46
|
+
_fetchData(): Promise<(TData & IConversationData) | undefined>;
|
|
47
|
+
_getConversation(): Promise<IConversation>;
|
|
48
|
+
_clearCache(): void;
|
|
49
|
+
}
|
package/dst/step.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const step_1 = tslib_1.__importDefault(require("@onereach/flow-sdk/dst/step"));
|
|
6
|
+
class ConvStep extends step_1.default {
|
|
7
|
+
get conversation() {
|
|
8
|
+
if (this.data.conversation == null)
|
|
9
|
+
throw new Error('missing conversation');
|
|
10
|
+
return this.data.conversation;
|
|
11
|
+
}
|
|
12
|
+
/** id of the thread where conversation data is stored */
|
|
13
|
+
get dataThreadId() {
|
|
14
|
+
const dataThreadId = this.state.thread ?? this.data.conversationThread;
|
|
15
|
+
return dataThreadId || this.thread.id;
|
|
16
|
+
}
|
|
17
|
+
get autoCancel() {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
get isGlobal() {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
get useQueue() {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
async fetchData() {
|
|
27
|
+
const data = await this._fetchData();
|
|
28
|
+
if (data?._conv == null)
|
|
29
|
+
throw new Error(`missing conversation data in state ${this.state.name}`);
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
async updateData() {
|
|
33
|
+
if (this.convDataCache == null)
|
|
34
|
+
throw new Error(`missing conversation cache in state ${this.state.name}`);
|
|
35
|
+
// console.log('UPDATE', this.conversation, this.thread.id, this.step.label, this.convDataCache?._conv.que)
|
|
36
|
+
const convDataThread = this.process.getSafeThread(this.dataThreadId);
|
|
37
|
+
await convDataThread.set(this.conversation, this.convDataCache);
|
|
38
|
+
}
|
|
39
|
+
async hasConversation() {
|
|
40
|
+
return (await this._fetchData())?._conv != null;
|
|
41
|
+
}
|
|
42
|
+
async runBefore() {
|
|
43
|
+
this.log.debug('conv.runBefore', { state: this.state, conv: this.conversation });
|
|
44
|
+
await super.runBefore();
|
|
45
|
+
// ensure cache is reset between step runs, just in case
|
|
46
|
+
this._clearCache();
|
|
47
|
+
this.triggers.local('_conv');
|
|
48
|
+
if (!await this.hasConversation())
|
|
49
|
+
return;
|
|
50
|
+
if (this.state.direct)
|
|
51
|
+
return;
|
|
52
|
+
if (this.event.action === 'cancel') {
|
|
53
|
+
this.state.name = 'onCancel';
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (this.useQueue && !await this.pushConvStep()) {
|
|
57
|
+
return await this.waitForConversation();
|
|
58
|
+
}
|
|
59
|
+
if (this.event.action == null && this.event.processed !== true && this.convDataCache?._conv?.trg?.[this.event.name] != null) {
|
|
60
|
+
if (this.isGlobal)
|
|
61
|
+
return;
|
|
62
|
+
const hasGlobal = await this.sendEventToStep({ event: { ...this.event, action: 'global' }, toGlobal: true }); // redirect to global (1)
|
|
63
|
+
this.log.debug('conv.sendEventToStep toGlobal', { hasGlobal });
|
|
64
|
+
if (hasGlobal != null) {
|
|
65
|
+
this.state.prevSkipName = this.state.name;
|
|
66
|
+
this.state.name = 'onSkipEvent';
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (this.isGlobal && this.event.action === 'global') {
|
|
71
|
+
return; // it was redirected from (1)
|
|
72
|
+
}
|
|
73
|
+
if (!this.isGlobal && (this.convDataCache?._conv?.glb?.length ?? 0) > 0) {
|
|
74
|
+
this.triggers.config({ params: { type: 'local' } });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async runAfter() {
|
|
78
|
+
if (!await this.hasConversation())
|
|
79
|
+
return await super.runAfter();
|
|
80
|
+
if (this.thread.hasControlAction()) {
|
|
81
|
+
if (await this.popConvStep()) {
|
|
82
|
+
await this.sendEventToStep({ toGlobal: this.isGlobal, action: 'activate' });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (this.isGlobal && this.event.processed !== true) {
|
|
86
|
+
await this.sendEventToStep({ toGlobal: false, event: { ...this.event, processed: undefined, action: 'local' } });
|
|
87
|
+
}
|
|
88
|
+
await super.runAfter();
|
|
89
|
+
}
|
|
90
|
+
async hasActiveGlobal() {
|
|
91
|
+
const globalLength = (await this._getConversation())?.glb?.length ?? 0;
|
|
92
|
+
return globalLength > 0;
|
|
93
|
+
}
|
|
94
|
+
async setTriggerType() {
|
|
95
|
+
if (!this.isGlobal && !await this.hasActiveGlobal()) {
|
|
96
|
+
this.triggers.config({ params: { type: 'local' } });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async waitForConversation() {
|
|
100
|
+
if (this.state.name !== 'waitForConversation') {
|
|
101
|
+
this.log.debug(this.state, 'conv.begin waitForConversation');
|
|
102
|
+
this.state.prevWaitName = this.state.name;
|
|
103
|
+
this.state.name = 'waitForConversation';
|
|
104
|
+
}
|
|
105
|
+
else if (this.event.name === '_conv') {
|
|
106
|
+
this.log.debug(this.state, 'conv.end waitForConversation');
|
|
107
|
+
this.gotoState({ ...this.state, name: this.state.prevWaitName, prevWaitName: undefined });
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.log.debug(this.state, 'conv.skip waitForConversation');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async onSkipEvent() {
|
|
114
|
+
this.log.debug('onSkipEvent');
|
|
115
|
+
this.state.name = this.state.prevSkipName;
|
|
116
|
+
delete this.state.prevSkipName;
|
|
117
|
+
}
|
|
118
|
+
async cancel() {
|
|
119
|
+
if (this.isGlobal) {
|
|
120
|
+
this.log.debug('conv.cannot cancel global');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const conv = await this._getConversation();
|
|
124
|
+
const activeStep = await this.activeStep(false);
|
|
125
|
+
if (activeStep != null) {
|
|
126
|
+
this.log.debug('conv.cancel', activeStep);
|
|
127
|
+
conv.lcl.splice(0);
|
|
128
|
+
await this.updateData();
|
|
129
|
+
await this.sendEventToStep({ toGlobal: false, action: 'cancel', toStep: activeStep });
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
this.log.debug('conv.nothing to cancel');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async onCancel() {
|
|
136
|
+
if (this.getExitStepId('cancel')) {
|
|
137
|
+
this.log.debug('conv.cancel exit');
|
|
138
|
+
this.exitStep('cancel');
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const conversationThread = this.state.thread ?? this.thread.id;
|
|
142
|
+
if (conversationThread === this.thread.id) {
|
|
143
|
+
this.log.debug('conv.cancel wait for end', this.conversation);
|
|
144
|
+
this.gotoState({ ...this.state, name: 'waitCancelEnd', direct: true });
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
this.log.debug('conv.cancel end', this.conversation);
|
|
148
|
+
this.end();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async waitCancelEnd() {
|
|
153
|
+
this.exitFlow();
|
|
154
|
+
}
|
|
155
|
+
async pause() {
|
|
156
|
+
const conv = await this._getConversation();
|
|
157
|
+
const activeStep = await this.activeStep(false);
|
|
158
|
+
if (activeStep != null) {
|
|
159
|
+
this.log.debug('conv.pause', activeStep);
|
|
160
|
+
conv.stk.push(conv.lcl.splice(0)); // save que to stk
|
|
161
|
+
await this.sendEventToStep({ toGlobal: false, action: 'pause', toStep: activeStep });
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
this.log.debug('conv.nothing to pause');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
async resume() {
|
|
168
|
+
const conv = await this._getConversation();
|
|
169
|
+
if (conv.stk.length > 0) {
|
|
170
|
+
conv.lcl.unshift(...(conv.stk.pop() ?? [])); // restore que from stk
|
|
171
|
+
await this.sendEventToStep({ toGlobal: false, action: 'resume' });
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this.log.debug('conv.nothing to resume');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async sendEventToStep({ toGlobal, toStep, action, event = { name: '_conv', action } }) {
|
|
178
|
+
toStep || (toStep = await this.activeStep(toGlobal));
|
|
179
|
+
if (toStep != null) {
|
|
180
|
+
const target = { type: 'session', name: toStep.key, id: toStep.thread };
|
|
181
|
+
return await this.thread.eventManager.emit(event, { invocationType: 'async', target });
|
|
182
|
+
}
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
async activeStep(isGlobal = this.isGlobal) {
|
|
186
|
+
const conv = await this._getConversation();
|
|
187
|
+
return isGlobal ? conv.glb[conv.glb.length - 1] : conv.lcl[0];
|
|
188
|
+
}
|
|
189
|
+
/** @returns true if current conv is active */
|
|
190
|
+
async pushConvStep() {
|
|
191
|
+
const conv = await this._getConversation();
|
|
192
|
+
const sessionKey = this.session.key;
|
|
193
|
+
if (sessionKey == null)
|
|
194
|
+
throw new Error('session is required');
|
|
195
|
+
const que = this.isGlobal ? conv.glb : conv.lcl;
|
|
196
|
+
const queIndex = que.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread);
|
|
197
|
+
if (queIndex < 0) {
|
|
198
|
+
if (que.length > 0 && !this.isGlobal && (this.autoCancel ?? conv.acl ?? false))
|
|
199
|
+
await this.cancel();
|
|
200
|
+
que.push({ key: sessionKey, thread: this.thread.id });
|
|
201
|
+
await this.updateData();
|
|
202
|
+
return this.isGlobal ? true : que.length === 1;
|
|
203
|
+
}
|
|
204
|
+
return queIndex === (this.isGlobal ? que.length - 1 : 0);
|
|
205
|
+
}
|
|
206
|
+
/** @returns true if current conv was active */
|
|
207
|
+
async popConvStep() {
|
|
208
|
+
const conv = await this._getConversation();
|
|
209
|
+
const sessionKey = this.session.key;
|
|
210
|
+
if (sessionKey == null)
|
|
211
|
+
throw new Error('session is required');
|
|
212
|
+
const que = this.isGlobal ? conv.glb : conv.lcl;
|
|
213
|
+
const queIndex = que.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread);
|
|
214
|
+
if (queIndex >= 0) {
|
|
215
|
+
// console.log('SPLICE1', this.thread.id, this.step.label, que)
|
|
216
|
+
que.splice(queIndex, 1);
|
|
217
|
+
await this.updateData();
|
|
218
|
+
return this.isGlobal ? queIndex === que.length : queIndex === 0;
|
|
219
|
+
}
|
|
220
|
+
let updated = false;
|
|
221
|
+
conv.stk.forEach((steps, stepsIdx) => {
|
|
222
|
+
const stpIdx = steps.findIndex(stp => stp.key === sessionKey && this.thread.id === stp.thread);
|
|
223
|
+
if (stpIdx >= 0) {
|
|
224
|
+
if (steps.length > 1)
|
|
225
|
+
steps.splice(stpIdx, 1);
|
|
226
|
+
else
|
|
227
|
+
conv.stk.splice(stepsIdx, 1);
|
|
228
|
+
updated = true;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
if (updated)
|
|
232
|
+
await this.updateData();
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
async startConversation(data, { autoCancel = false, events }) {
|
|
236
|
+
this.convDataCache = {
|
|
237
|
+
...data,
|
|
238
|
+
_conv: {
|
|
239
|
+
acl: (autoCancel ? 1 : undefined),
|
|
240
|
+
trg: events,
|
|
241
|
+
glb: [],
|
|
242
|
+
stk: [],
|
|
243
|
+
lcl: []
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
await this.updateData();
|
|
247
|
+
// this.state.thread = this.dataThreadId
|
|
248
|
+
}
|
|
249
|
+
async _fetchData() {
|
|
250
|
+
if (this.convDataCache != null)
|
|
251
|
+
return this.convDataCache;
|
|
252
|
+
const convDataThread = this.process.getSafeThread(this.dataThreadId);
|
|
253
|
+
this.convDataCache = await convDataThread.get(this.conversation);
|
|
254
|
+
// console.log('FETCH', this.conversation, this.thread.id, this.step.label, this.convDataCache?._conv.que)
|
|
255
|
+
return this.convDataCache;
|
|
256
|
+
}
|
|
257
|
+
async _getConversation() {
|
|
258
|
+
return (await this.fetchData())._conv;
|
|
259
|
+
}
|
|
260
|
+
_clearCache() {
|
|
261
|
+
this.convDataCache = undefined;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
exports.default = ConvStep;
|
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 '
|
|
2
|
+
import ConvStep from './step';
|
|
3
3
|
import BasicError from '@onereach/flow-sdk/dst/errors/base';
|
|
4
4
|
export declare type TODO = any;
|
|
5
5
|
export interface IVoiceChannel {
|
|
@@ -16,20 +16,25 @@ export interface IVoiceChannel {
|
|
|
16
16
|
asr?: TODO;
|
|
17
17
|
tts?: TODO;
|
|
18
18
|
sessionEndedBy?: string;
|
|
19
|
+
to?: string;
|
|
20
|
+
from?: string;
|
|
21
|
+
headers?: Record<string, string>;
|
|
19
22
|
}
|
|
20
|
-
export declare type EventType = 'hangup' | 'error' | 'cancel' | 'avm-detected' | 'recognition' | 'digit' | 'digits' | 'conference-start' | 'conference-end' | 'playback' | 'timeout' | 'record' | 'bridge' | 'bridge/ended';
|
|
23
|
+
export declare type EventType = 'hangup' | 'error' | 'cancel' | 'avm-detected' | 'recognition' | 'digit' | 'digits' | 'conference-start' | 'conference-end' | 'playback' | 'timeout' | 'record' | 'bridge' | 'bridge/ended' | 'is_flow_ready' | 'call';
|
|
21
24
|
export declare class VoiceStepError extends BasicError {
|
|
22
25
|
}
|
|
23
26
|
export interface VoiceEvent {
|
|
24
27
|
type: EventType;
|
|
25
28
|
error?: Error;
|
|
26
29
|
}
|
|
30
|
+
export interface CallStartEvent extends VoiceEvent {
|
|
31
|
+
channel: IVoiceChannel;
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
}
|
|
27
34
|
export default class VoiceStep<TIn = unknown, TOut = unknown, TParams = VoiceEvent> extends ConvStep<IVoiceChannel, TIn & {
|
|
28
35
|
handleCancel?: boolean;
|
|
29
36
|
}, TOut, TParams> {
|
|
30
37
|
get autoCancel(): boolean | undefined;
|
|
31
|
-
onCancel(): Promise<void>;
|
|
32
|
-
waitForEnd(): Promise<void>;
|
|
33
38
|
sendCommands({ id, type, callback }: IVoiceChannel, commands: TODO[]): Promise<unknown>;
|
|
34
39
|
extractSectionMessages(sections: IPromtpSection[]): string;
|
|
35
40
|
extractSectionFiles(sections: IPromtpSection[]): Array<{
|
package/dst/voice.js
CHANGED
|
@@ -5,7 +5,8 @@ exports.VoiceStepError = void 0;
|
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
6
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
7
7
|
const uuid_1 = require("uuid");
|
|
8
|
-
|
|
8
|
+
// TODO: !!!!! import ConvStep from '@onereach/step-conversation/dst/step' !!!!!
|
|
9
|
+
const step_1 = tslib_1.__importDefault(require("./step"));
|
|
9
10
|
const base_1 = tslib_1.__importDefault(require("@onereach/flow-sdk/dst/errors/base"));
|
|
10
11
|
class VoiceStepError extends base_1.default {
|
|
11
12
|
}
|
|
@@ -15,17 +16,6 @@ class VoiceStep extends step_1.default {
|
|
|
15
16
|
get autoCancel() {
|
|
16
17
|
return true;
|
|
17
18
|
}
|
|
18
|
-
async onCancel() {
|
|
19
|
-
if (this.data.handleCancel === true) {
|
|
20
|
-
this.exitStep('cancel');
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
this.gotoState('waitForEnd');
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
async waitForEnd() {
|
|
27
|
-
this.exitFlow();
|
|
28
|
-
}
|
|
29
19
|
async sendCommands({ id, type, callback }, commands) {
|
|
30
20
|
if (lodash_1.default.isEmpty(commands))
|
|
31
21
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onereach/step-voice",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.16",
|
|
4
4
|
"author": "Roman Zolotarov <roman.zolotarov@onereach.com>",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Roman Zolotarov",
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"npm": ">= 6.0.0"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@onereach/step-conversation": "^1.0.
|
|
16
|
+
"@onereach/step-conversation": "^1.0.40",
|
|
17
17
|
"lodash": "^4.17.21",
|
|
18
18
|
"uuid": "^8.3.2"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@onereach/flow-sdk": "^3.
|
|
21
|
+
"@onereach/flow-sdk": "^3.1.3",
|
|
22
22
|
"@swc/cli": "^0.1.57",
|
|
23
23
|
"@swc/core": "^1.2.197",
|
|
24
24
|
"@swc/jest": "^0.2.21",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"@types/lodash": "^4.14.182",
|
|
27
27
|
"@types/timestring": "^6.0.2",
|
|
28
28
|
"@types/uuid": "^8.3.4",
|
|
29
|
-
"aws-sdk": "^2.
|
|
29
|
+
"aws-sdk": "^2.1151.0",
|
|
30
30
|
"babel-runtime": "^6.26.0",
|
|
31
31
|
"docdash": "^1.2.0",
|
|
32
32
|
"husky": "^8.0.1",
|