@onereach/step-voice 4.0.37 → 4.0.39
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/Custom Voice Input.d.ts +35 -0
- package/dst/Custom Voice Input.js +213 -0
- package/dst/Transfer.d.ts +1 -1
- package/dst/Transfer.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import VoiceStep, { TODO, VoiceEvent } from './voice';
|
|
2
|
+
interface INPUT {
|
|
3
|
+
textType: string;
|
|
4
|
+
asr: TODO;
|
|
5
|
+
tts: TODO;
|
|
6
|
+
audio: TODO[];
|
|
7
|
+
choices: TODO[];
|
|
8
|
+
prompts: TODO[];
|
|
9
|
+
sensitiveData: {
|
|
10
|
+
muteStep: boolean;
|
|
11
|
+
muteUser: boolean;
|
|
12
|
+
muteBot: boolean;
|
|
13
|
+
};
|
|
14
|
+
noReplyDelay: number;
|
|
15
|
+
promptsTriggers: TODO[];
|
|
16
|
+
usePromptsTriggers: boolean;
|
|
17
|
+
recognitionModel?: string;
|
|
18
|
+
resultInterpretation?: string;
|
|
19
|
+
phraseList: Array<{
|
|
20
|
+
phrase: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
interface EVENT extends VoiceEvent {
|
|
24
|
+
callRecording?: TODO;
|
|
25
|
+
exitId?: string;
|
|
26
|
+
digit?: string;
|
|
27
|
+
digits?: string;
|
|
28
|
+
phrases?: TODO[];
|
|
29
|
+
tags?: string[];
|
|
30
|
+
out?: string;
|
|
31
|
+
}
|
|
32
|
+
export default class CustomVoiceInput extends VoiceStep<INPUT, {}, EVENT> {
|
|
33
|
+
runStep(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
5
|
+
const voice_1 = tslib_1.__importDefault(require("./voice"));
|
|
6
|
+
const getPhrasesForSpeechRec = (choices) => {
|
|
7
|
+
const texts = lodash_1.default.flatMap(choices, (choice) => choice.options.map((opt) => opt.text));
|
|
8
|
+
return texts;
|
|
9
|
+
};
|
|
10
|
+
const getAsrSettings = (settings, choices, recognitionModel) => {
|
|
11
|
+
if (settings.engine === 'google' || settings.engine === 'google_beta') {
|
|
12
|
+
return {
|
|
13
|
+
...settings,
|
|
14
|
+
config: {
|
|
15
|
+
speechContexts: [
|
|
16
|
+
{
|
|
17
|
+
phrases: getPhrasesForSpeechRec(choices)
|
|
18
|
+
}
|
|
19
|
+
],
|
|
20
|
+
recognitionModel
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return settings;
|
|
25
|
+
};
|
|
26
|
+
const isRepromptTrigger = (recogResult, promptsTriggers) => {
|
|
27
|
+
const phrases = recogResult.map((r) => {
|
|
28
|
+
return r.lexical;
|
|
29
|
+
});
|
|
30
|
+
const triggers = promptsTriggers.flatMap((trigger) => {
|
|
31
|
+
if (lodash_1.default.isEmpty(trigger.grammar)) {
|
|
32
|
+
return trigger.text.replace(/`/g, '');
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return trigger.grammar.value.map((v) => {
|
|
36
|
+
return v.replace(/`/g, '');
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
return !lodash_1.default.isEmpty(phrases.filter((e) => triggers.includes(e)));
|
|
41
|
+
};
|
|
42
|
+
class CustomVoiceInput extends voice_1.default {
|
|
43
|
+
async runStep() {
|
|
44
|
+
const channel = await this.fetchData();
|
|
45
|
+
const { textType, asr, tts, sensitiveData, noReplyDelay, usePromptsTriggers, recognitionModel } = this.data;
|
|
46
|
+
const exitExists = (exitId) => {
|
|
47
|
+
return lodash_1.default.some(choices, (choice) => choice.exitId === exitId);
|
|
48
|
+
};
|
|
49
|
+
const mainLeg = {
|
|
50
|
+
exitId: '5886d1ee-db6d-45ca-a2ce-d586862c65ff',
|
|
51
|
+
dtmf: null,
|
|
52
|
+
grxml: '',
|
|
53
|
+
text: `.+`,
|
|
54
|
+
grammar: {},
|
|
55
|
+
};
|
|
56
|
+
const choices = this.buildChoices({ choices: [...this.data.choices, mainLeg] });
|
|
57
|
+
const ttsSettings = tts.getSettings(channel.tts);
|
|
58
|
+
const asrSettings = getAsrSettings(asr.getSettings(channel.asr), choices, recognitionModel);
|
|
59
|
+
const repromptsList = this.buildReprompts({ prompts: this.data.prompts });
|
|
60
|
+
const speechSections = this.buildSections({ sections: this.data.audio, textType, ttsSettings });
|
|
61
|
+
const grammar = {
|
|
62
|
+
id: this.currentStepId,
|
|
63
|
+
choices,
|
|
64
|
+
asr: asrSettings
|
|
65
|
+
};
|
|
66
|
+
const command = {
|
|
67
|
+
name: 'double-asr',
|
|
68
|
+
params: {
|
|
69
|
+
grammar,
|
|
70
|
+
sections: [],
|
|
71
|
+
resultInterpretation: this.data.resultInterpretation,
|
|
72
|
+
tokenPhrases: this.data.phraseList.map(item => item.phrase)
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
// There's a specific need to do so. There might be ${variable} section
|
|
76
|
+
this.triggers.once(`in/voice/${channel.id}/event`, async (event) => {
|
|
77
|
+
// await thisStep.refreshCallback(this, conversation, channel); -- ???
|
|
78
|
+
if (channel.recordCall && sensitiveData?.muteStep) {
|
|
79
|
+
await this.sendCommands(channel, [{
|
|
80
|
+
name: 'resume-record-session',
|
|
81
|
+
params: {
|
|
82
|
+
muteUser: sensitiveData.muteUser,
|
|
83
|
+
muteBot: sensitiveData.muteBot
|
|
84
|
+
}
|
|
85
|
+
}]);
|
|
86
|
+
}
|
|
87
|
+
const reportingSettingsKey = this.rptsStarted ? 'transcriptRepromptResponse' : 'transcriptResponse';
|
|
88
|
+
switch (event.params.type) {
|
|
89
|
+
// digit recognition removed
|
|
90
|
+
case 'recognition': {
|
|
91
|
+
const params = event.params;
|
|
92
|
+
const exitId = params.exitId;
|
|
93
|
+
const phrases = params.phrases;
|
|
94
|
+
if (lodash_1.default.isEmpty(phrases)) {
|
|
95
|
+
return this.exitStep('unrecognized', {});
|
|
96
|
+
}
|
|
97
|
+
const voiceProcessResult = lodash_1.default.chain(phrases)
|
|
98
|
+
.map((p) => p.lexical)
|
|
99
|
+
.join(' | ')
|
|
100
|
+
.value();
|
|
101
|
+
// On bargeIn, we should stop playback
|
|
102
|
+
if (exitExists(exitId)) {
|
|
103
|
+
await this.transcript(channel, {
|
|
104
|
+
message: lodash_1.default.replace(this.getExitStepLabel(exitId) ?? '', /^(\d+\. )?(~ )?/, ''),
|
|
105
|
+
voiceProcessResult,
|
|
106
|
+
reportingSettingsKey,
|
|
107
|
+
action: 'Call Recognition',
|
|
108
|
+
actionFromBot: false
|
|
109
|
+
});
|
|
110
|
+
// There might be hooks after this step which we will try to avoid
|
|
111
|
+
return this.exitStep(exitId, this.exitChoiceData('voice', params));
|
|
112
|
+
}
|
|
113
|
+
else if (this.rptsHasMore({ repromptsList }) &&
|
|
114
|
+
(usePromptsTriggers ? isRepromptTrigger(phrases, this.data.promptsTriggers) : true)) {
|
|
115
|
+
await this.transcript(channel, {
|
|
116
|
+
message: 'Unrecognized',
|
|
117
|
+
voiceProcessResult,
|
|
118
|
+
reportingSettingsKey,
|
|
119
|
+
action: 'Call Recognition',
|
|
120
|
+
actionFromBot: false
|
|
121
|
+
});
|
|
122
|
+
await this.rptsSend(channel, {
|
|
123
|
+
command,
|
|
124
|
+
repromptsList,
|
|
125
|
+
noReplyDelay,
|
|
126
|
+
speechSections,
|
|
127
|
+
textType,
|
|
128
|
+
ttsSettings
|
|
129
|
+
});
|
|
130
|
+
return this.exitFlow();
|
|
131
|
+
}
|
|
132
|
+
// There's no more reprompts, should move on to unrecognized direction
|
|
133
|
+
await this.transcript(channel, {
|
|
134
|
+
message: 'Not Recognized',
|
|
135
|
+
voiceProcessResult,
|
|
136
|
+
reportingSettingsKey,
|
|
137
|
+
action: 'Call Recognition',
|
|
138
|
+
actionFromBot: false
|
|
139
|
+
});
|
|
140
|
+
// We might end up in same session
|
|
141
|
+
return this.exitStep('unrecognized', this.exitChoiceData('voice', params));
|
|
142
|
+
}
|
|
143
|
+
case 'timeout': {
|
|
144
|
+
if (this.rptsHasMore({ repromptsList })) {
|
|
145
|
+
await this.transcript(channel, {
|
|
146
|
+
message: 'No Reply',
|
|
147
|
+
reportingSettingsKey,
|
|
148
|
+
action: 'Call Prompt',
|
|
149
|
+
actionFromBot: false
|
|
150
|
+
});
|
|
151
|
+
await this.rptsSend(channel, {
|
|
152
|
+
command,
|
|
153
|
+
repromptsList,
|
|
154
|
+
noReplyDelay,
|
|
155
|
+
speechSections,
|
|
156
|
+
textType,
|
|
157
|
+
ttsSettings
|
|
158
|
+
});
|
|
159
|
+
return this.exitFlow();
|
|
160
|
+
}
|
|
161
|
+
await this.transcript(channel, {
|
|
162
|
+
message: 'Not Replied',
|
|
163
|
+
reportingSettingsKey,
|
|
164
|
+
action: 'Call Prompt',
|
|
165
|
+
actionFromBot: false
|
|
166
|
+
});
|
|
167
|
+
// We might end up in same session
|
|
168
|
+
return this.exitStep('no reply', {});
|
|
169
|
+
}
|
|
170
|
+
case 'hangup': {
|
|
171
|
+
await this.handleHangup(channel);
|
|
172
|
+
return await this.waitConvEnd();
|
|
173
|
+
}
|
|
174
|
+
// case 'cancel': {
|
|
175
|
+
// return this.data.handleCancel === true && this.exitStep('cancel')
|
|
176
|
+
// }
|
|
177
|
+
case 'error':
|
|
178
|
+
return this.throwError(event.params.error);
|
|
179
|
+
default:
|
|
180
|
+
return this.exitFlow();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
this.triggers.otherwise(async () => {
|
|
184
|
+
await this.transcript(channel, {
|
|
185
|
+
sections: speechSections,
|
|
186
|
+
reprompt: {
|
|
187
|
+
maxAttempts: repromptsList.length,
|
|
188
|
+
attempt: 0
|
|
189
|
+
},
|
|
190
|
+
reportingSettingsKey: 'transcriptPrompt',
|
|
191
|
+
action: 'Call Prompt',
|
|
192
|
+
actionFromBot: true
|
|
193
|
+
});
|
|
194
|
+
command.params.sections = speechSections;
|
|
195
|
+
command.params.timeout = this.rptsTimeout({ noReplyDelay, repromptsList });
|
|
196
|
+
await this.sendCommands(channel, [
|
|
197
|
+
...channel.recordCall && sensitiveData?.muteStep
|
|
198
|
+
? [{
|
|
199
|
+
name: 'stop-record-session',
|
|
200
|
+
params: {
|
|
201
|
+
muteUser: sensitiveData.muteUser,
|
|
202
|
+
muteBot: sensitiveData.muteBot
|
|
203
|
+
}
|
|
204
|
+
}]
|
|
205
|
+
: [],
|
|
206
|
+
command
|
|
207
|
+
]);
|
|
208
|
+
return this.exitFlow();
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
exports.default = CustomVoiceInput;
|
|
213
|
+
// --------------------------------------------------------------------
|
package/dst/Transfer.d.ts
CHANGED
package/dst/Transfer.js
CHANGED
|
@@ -48,7 +48,7 @@ class Transfer extends voice_1.default {
|
|
|
48
48
|
return this.exitFlow();
|
|
49
49
|
});
|
|
50
50
|
this.triggers.otherwise(async () => {
|
|
51
|
-
const { phoneNumber, sessionTimeout, destination, sipHeaders = [], sipHost, sipUser, sipPassword, refer, gatewaySettigsMode,
|
|
51
|
+
const { phoneNumber, sessionTimeout, destination, sipHeaders = [], sipHost, sipUser, sipPassword, refer, gatewaySettigsMode, from } = this.data;
|
|
52
52
|
const destinationIsSip = (/^sip:/i).test(destination);
|
|
53
53
|
const destinationIsUser = (/^user:/i).test(destination);
|
|
54
54
|
const callerID = phoneNumber;
|
|
@@ -58,7 +58,7 @@ class Transfer extends voice_1.default {
|
|
|
58
58
|
const command = {
|
|
59
59
|
name: destinationIsSip ? (refer ? 'refer' : 'bridge_sip') : (destinationIsUser ? 'bridge_user' : 'bridge'),
|
|
60
60
|
params: {
|
|
61
|
-
botNumber: inheritGatewaySetting ?
|
|
61
|
+
botNumber: inheritGatewaySetting ? from : channel.botNumber,
|
|
62
62
|
transferFrom: callerID,
|
|
63
63
|
transferTo: destination,
|
|
64
64
|
headers,
|