@onereach/step-voice 7.0.9-processttschunk.0 → 7.0.9-processttschunk.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.
@@ -12,6 +12,8 @@ interface INPUT {
12
12
  recordFileTtl: number;
13
13
  limitLinkTtl: boolean;
14
14
  recordLinkTtl: number;
15
+ loopPrevention_enabled: boolean;
16
+ loopPrevention_maxLoops: number;
15
17
  }
16
18
  interface OUTPUT {
17
19
  type: string;
@@ -93,6 +93,14 @@ class GlobalCommand extends voice_1.default {
93
93
  await this.initGrammar(call);
94
94
  }
95
95
  });
96
+ // Set loop prevention settings on call object
97
+ if (this.data.loopPrevention_enabled) {
98
+ this.session.data.loopPrevention = {
99
+ enabled: this.data.loopPrevention_enabled,
100
+ maxLoops: this.data.loopPrevention_maxLoops
101
+ };
102
+ await this.updateData();
103
+ }
96
104
  if (allowAck) {
97
105
  this.triggers.local('ack', ({ params: { ack } }) => {
98
106
  const worker = this.process.getThread(this.workerThreadId);
@@ -1,4 +1,15 @@
1
1
  import VoiceStep, { CallStartEvent, TODO } from './voice';
2
+ declare const enum GATEWAY_SETTINGS_MODE {
3
+ DEFAULT = "default",
4
+ CUSTOM = "custom",
5
+ INHERIT = "inherit",
6
+ PROFILE = "profile"
7
+ }
8
+ declare const enum SIP_PROFILE {
9
+ DEFAULT = "default",
10
+ INTERNAL = "internal",
11
+ EXTERNAL = "external"
12
+ }
2
13
  interface INPUT {
3
14
  callId?: string;
4
15
  sessionTimeout?: number | string;
@@ -8,9 +19,11 @@ interface INPUT {
8
19
  tts: TODO;
9
20
  from: string;
10
21
  endUserNumber: string;
22
+ gatewaySettingsMode?: GATEWAY_SETTINGS_MODE;
11
23
  sipHost?: string;
12
24
  sipUser?: string;
13
25
  sipPassword?: string;
26
+ sipProfile?: SIP_PROFILE;
14
27
  timeout?: number | string;
15
28
  headers?: Array<{
16
29
  name: string;
@@ -19,6 +32,10 @@ interface INPUT {
19
32
  enableSpoofCallerId?: boolean;
20
33
  spoofCallerId?: string;
21
34
  isAMD?: boolean;
35
+ enableWhisperTransfer?: boolean;
36
+ whisperAnnounceAudio?: TODO[];
37
+ whisperCustomerConversation?: string;
38
+ whisperCustomerConversationThread?: string;
22
39
  }
23
40
  export default class InitiateCall extends VoiceStep<INPUT, TODO, CallStartEvent> {
24
41
  get conversation(): string | import("@onereach/flow-sdk/types").IMergeField;
@@ -47,7 +47,7 @@ class InitiateCall extends voice_1.default {
47
47
  this.exitStep('cancel');
48
48
  }
49
49
  async waitForCall() {
50
- const { asr, tts, from: botNumber, endUserNumber, sipHost, sipUser, sipPassword, timeout, headers, enableSpoofCallerId, spoofCallerId, isAMD, otherCallRef, otherCallRefThread, handleCancel } = this.data;
50
+ const { asr, tts, from: botNumber, endUserNumber, gatewaySettingsMode, sipHost, sipUser, sipPassword, sipProfile, timeout, headers, enableSpoofCallerId, spoofCallerId, isAMD, otherCallRef, otherCallRefThread, handleCancel, } = this.data;
51
51
  const call = await this.fetchData();
52
52
  this.triggers.once(`in/voice/${call.id}/event`, async (event) => {
53
53
  switch (event.params.type) {
@@ -84,7 +84,7 @@ class InitiateCall extends voice_1.default {
84
84
  reportingSettingsKey: 'transcript',
85
85
  actionFromBot: true
86
86
  });
87
- await this.sendCommands(newCall, [
87
+ const commands = [
88
88
  ...isAMD
89
89
  ? [{
90
90
  name: 'start-avmd',
@@ -100,8 +100,9 @@ class InitiateCall extends voice_1.default {
100
100
  type: asr.serverSettings.engine
101
101
  }
102
102
  }]
103
- : []
104
- ]);
103
+ : [],
104
+ ];
105
+ await this.sendCommands(newCall, commands);
105
106
  return this.exitStep('success');
106
107
  }
107
108
  case 'hangup': {
@@ -160,6 +161,23 @@ class InitiateCall extends voice_1.default {
160
161
  memo[header.name] = `${header.value}`;
161
162
  return memo;
162
163
  }, {});
164
+ // GET SIP PROFILE SETTINGS
165
+ let gateway;
166
+ const profile = sipProfile !== "default" /* SIP_PROFILE.DEFAULT */ ? sipProfile : undefined;
167
+ switch (gatewaySettingsMode) {
168
+ case "custom" /* GATEWAY_SETTINGS_MODE.CUSTOM */:
169
+ gateway = {
170
+ host: sipHost,
171
+ user: sipUser,
172
+ username: sipUser,
173
+ password: sipPassword,
174
+ profile
175
+ };
176
+ break;
177
+ case "profile" /* GATEWAY_SETTINGS_MODE.PROFILE */:
178
+ gateway = gateway = profile != null ? { profile } : undefined;
179
+ break;
180
+ }
163
181
  const params = {
164
182
  id: call.id,
165
183
  from: botNumber,
@@ -169,16 +187,11 @@ class InitiateCall extends voice_1.default {
169
187
  enableSpoofCallerId,
170
188
  spoofCallerId
171
189
  },
172
- gateway: sipHost
173
- ? {
174
- host: sipHost,
175
- username: sipUser,
176
- password: sipPassword
177
- }
178
- : undefined,
179
190
  timeout: originateTimeout,
180
191
  version: 2,
181
- sessionExpireTime: this.session.expireTime
192
+ sessionExpireTime: this.session.expireTime,
193
+ gateway,
194
+ maxLoops: this.session.data?.loopPrevention?.enabled && this.session.data.loopPrevention.maxLoops,
182
195
  };
183
196
  if (otherCallRef) {
184
197
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
@@ -2,6 +2,7 @@ import VoiceStep, { TODO, VoiceEvent } from './voice';
2
2
  interface INPUT {
3
3
  conferenceName: string;
4
4
  stayInConference: boolean;
5
+ mute: boolean;
5
6
  volumeIn?: number;
6
7
  volumeOut?: number;
7
8
  }
@@ -6,7 +6,7 @@ const voice_1 = tslib_1.__importDefault(require("./voice"));
6
6
  class JoinConference extends voice_1.default {
7
7
  async runStep() {
8
8
  const call = await this.fetchData();
9
- const { conferenceName, stayInConference, volumeIn, volumeOut } = this.data;
9
+ const { conferenceName, stayInConference, volumeIn, volumeOut, mute } = this.data;
10
10
  this.triggers.local(`in/voice/${call.id}`, async (event) => {
11
11
  switch (event.params.type) {
12
12
  case 'hangup':
@@ -49,7 +49,10 @@ class JoinConference extends voice_1.default {
49
49
  name: 'conference.start',
50
50
  params: {
51
51
  room: conferenceName,
52
- flags: [call.vv >= 3 ? 'mandatory_member_endconf' : 'endconf'],
52
+ flags: [
53
+ call.vv >= 3 ? 'mandatory_member_endconf' : 'endconf',
54
+ ...mute ? ['mute'] : []
55
+ ],
53
56
  stayInConference,
54
57
  volumeIn,
55
58
  volumeOut
@@ -46,7 +46,8 @@ class SayMessage extends voice_1.default {
46
46
  name: 'speak',
47
47
  params: {
48
48
  sections: speechSections,
49
- reporterTranscriptEventId: ''
49
+ reporterTranscriptEventId: '',
50
+ useWhisperFeature: true
50
51
  }
51
52
  };
52
53
  const eventId = await this.transcript(call, {
@@ -37,6 +37,15 @@ class StartTTSChunksProcessing extends voice_1.default {
37
37
  this.log.info('tts-chunks-playback-done', event.params);
38
38
  return this.exitStep('next', {}, false);
39
39
  }
40
+ case 'tts-audio/started':
41
+ case 'tts-audio/finished':
42
+ case 'tts-audio/interrupted':
43
+ case 'asr-input/started':
44
+ case 'asr-input/ended':
45
+ case 'asr-input/processed': {
46
+ this.log.warn('Received asr/tts status event', event.params);
47
+ return;
48
+ }
40
49
  case 'timeout': {
41
50
  this.log.info('timeout', event.params);
42
51
  return this.exitStep(event.params.type, {}, false);
package/dst/Transfer.d.ts CHANGED
@@ -1,4 +1,15 @@
1
1
  import VoiceStep, { VoiceEvent } from './voice';
2
+ declare const enum GATEWAY_SETTINGS_MODE {
3
+ DEFAULT = "default",
4
+ CUSTOM = "custom",
5
+ INHERIT = "inherit",
6
+ PROFILE = "profile"
7
+ }
8
+ declare const enum SIP_PROFILE {
9
+ DEFAULT = "default",
10
+ INTERNAL = "internal",
11
+ EXTERNAL = "external"
12
+ }
2
13
  interface INPUT {
3
14
  destination: string;
4
15
  phoneNumber: string;
@@ -8,11 +19,15 @@ interface INPUT {
8
19
  value: string;
9
20
  }>;
10
21
  refer: boolean;
11
- gatewaySettigsMode?: string;
22
+ withReplaces: boolean;
23
+ replacesConversation?: string;
24
+ replacesConversationThread?: string;
12
25
  from?: string;
26
+ gatewaySettingsMode?: GATEWAY_SETTINGS_MODE;
13
27
  sipHost?: string;
14
28
  sipUser?: string;
15
29
  sipPassword?: string;
30
+ sipProfile?: SIP_PROFILE;
16
31
  muteRecording?: boolean;
17
32
  }
18
33
  interface EVENT extends VoiceEvent {
package/dst/Transfer.js CHANGED
@@ -52,33 +52,77 @@ class Transfer extends voice_1.default {
52
52
  return this.exitFlow();
53
53
  });
54
54
  this.triggers.otherwise(async () => {
55
- const { phoneNumber, sessionTimeout, destination, sipHeaders = [], sipHost, sipUser, sipPassword, refer, gatewaySettigsMode, from, muteRecording } = this.data;
55
+ const { phoneNumber, sessionTimeout, destination, sipHeaders = [], sipHost, sipUser, sipPassword, sipProfile, refer, withReplaces, replacesConversation, replacesConversationThread, gatewaySettingsMode, from, muteRecording, } = this.data;
56
56
  const destinationIsSip = (/^sip:/i).test(destination);
57
57
  const destinationIsUser = (/^user:/i).test(destination);
58
58
  const callerID = phoneNumber;
59
59
  const timeout = Number(sessionTimeout);
60
- const inheritGatewaySetting = gatewaySettigsMode === 'inherit';
60
+ const inheritGatewaySetting = gatewaySettingsMode === 'inherit';
61
61
  const headers = Object.fromEntries(sipHeaders.map(({ name, value }) => [name, value]));
62
+ let transferFrom = callerID;
63
+ let transferTo = destination;
64
+ let replacesDialog;
65
+ if (refer && withReplaces && replacesConversation) {
66
+ const replacesCall = replacesConversationThread
67
+ ? await this.process.getSafeThread(replacesConversationThread).get(replacesConversation)
68
+ : await this.getConversationByName(replacesConversation);
69
+ // For attended transfer: transferFrom becomes current call's ID,
70
+ // and replacesDialog carries the dialog identifiers for Replaces header
71
+ transferFrom = call.id;
72
+ replacesDialog = {
73
+ callId: replacesCall['toCallId'],
74
+ toTag: replacesCall['toTag'],
75
+ fromTag: replacesCall['fromTag'],
76
+ uri: replacesCall['toUri'],
77
+ };
78
+ }
79
+ // GET SIP PROFILE SETTINGS
80
+ let gateway;
81
+ const profile = sipProfile !== "default" /* SIP_PROFILE.DEFAULT */ ? sipProfile : undefined;
82
+ switch (gatewaySettingsMode) {
83
+ case "custom" /* GATEWAY_SETTINGS_MODE.CUSTOM */:
84
+ gateway = {
85
+ host: sipHost,
86
+ user: sipUser,
87
+ username: sipUser,
88
+ password: sipPassword,
89
+ profile
90
+ };
91
+ break;
92
+ case "profile" /* GATEWAY_SETTINGS_MODE.PROFILE */:
93
+ gateway = gateway = profile != null ? { profile } : undefined;
94
+ break;
95
+ }
96
+ let commandName = 'bridge';
97
+ if (destinationIsSip) {
98
+ commandName = refer ? 'refer' : 'bridge_sip';
99
+ }
100
+ else if (destinationIsUser) {
101
+ commandName = 'bridge_user';
102
+ }
103
+ const botNumber = inheritGatewaySetting ? from : call.botNumber;
104
+ const maxLoops = this.session.data?.loopPrevention?.enabled
105
+ ? this.session.data.loopPrevention.maxLoops
106
+ : undefined;
62
107
  const command = {
63
- name: destinationIsSip ? (refer ? 'refer' : 'bridge_sip') : (destinationIsUser ? 'bridge_user' : 'bridge'),
108
+ name: commandName,
64
109
  params: {
65
- botNumber: inheritGatewaySetting ? from : call.botNumber,
66
- transferFrom: callerID,
67
- transferTo: destination,
110
+ botNumber,
111
+ transferFrom,
112
+ transferTo,
68
113
  headers,
69
- gateway: sipHost
70
- ? {
71
- host: sipHost,
72
- user: sipUser,
73
- username: sipUser,
74
- password: sipPassword
75
- }
76
- : undefined,
77
- timeout
114
+ gateway,
115
+ timeout,
116
+ maxLoops,
117
+ ...(replacesDialog && { replacesDialog }),
78
118
  }
79
119
  };
80
120
  const muteAfterTransfer = !!muteRecording;
81
- await this.pauseRecording(call, command, { muteStep: muteAfterTransfer, muteBot: muteAfterTransfer, muteUser: muteAfterTransfer });
121
+ await this.pauseRecording(call, command, {
122
+ muteStep: muteAfterTransfer,
123
+ muteBot: muteAfterTransfer,
124
+ muteUser: muteAfterTransfer
125
+ });
82
126
  await this.transcript(call, {
83
127
  action: 'Transfer Start',
84
128
  actionFromBot: true,
package/dst/step.d.ts CHANGED
@@ -25,6 +25,7 @@ export default class ConvStep<TData extends IConversationData, TIn = unknown, TO
25
25
  get useQueue(): boolean;
26
26
  fetchData(): Promise<TData>;
27
27
  getConversation(): Promise<IConversation>;
28
+ getConversationByName(conversation: string): Promise<IConversation>;
28
29
  updateData(): Promise<void>;
29
30
  hasConversation(): Promise<boolean>;
30
31
  runBefore(): Promise<void>;
package/dst/step.js CHANGED
@@ -51,6 +51,10 @@ class ConvStep extends step_1.default {
51
51
  async getConversation() {
52
52
  return (await this.fetchData())._conv;
53
53
  }
54
+ async getConversationByName(conversation) {
55
+ const convDataThread = this.process.getSafeThread(this.dataThreadId);
56
+ return await convDataThread.get(conversation);
57
+ }
54
58
  async updateData() {
55
59
  if (this.convDataCache == null)
56
60
  throw new Error(`missing conversation cache in state ${this.state.name}`);
package/dst/voice.d.ts CHANGED
@@ -33,7 +33,7 @@ export interface IVoiceCall extends IConversationData {
33
33
  user: string;
34
34
  };
35
35
  }
36
- export type EventType = 'hangup' | 'ack' | 'error' | 'cancel' | 'background' | 'avm-detected' | 'recognition' | 'digit' | 'digits' | 'conference-start' | 'conference-end' | 'playback' | 'timeout' | 'record' | 'bridge' | 'bridge/ended' | 'is_flow_ready' | 'call' | 'dtmf-sent' | 'tts-chunks-playback-done';
36
+ export type EventType = 'hangup' | 'ack' | 'error' | 'cancel' | 'background' | 'avm-detected' | 'recognition' | 'digit' | 'digits' | 'conference-start' | 'conference-end' | 'playback' | 'timeout' | 'record' | 'bridge' | 'bridge/ended' | 'is_flow_ready' | 'call' | 'dtmf-sent' | 'tts-chunks-playback-done' | 'tts-audio/started' | 'tts-audio/finished' | 'tts-audio/interrupted' | 'asr-input/started' | 'asr-input/ended' | 'asr-input/processed';
37
37
  export declare class VoiceStepError extends BasicError {
38
38
  }
39
39
  export type VoicerError = Error & {
package/dst/voice.js CHANGED
@@ -76,12 +76,14 @@ class VoiceStep extends step_1.default {
76
76
  }
77
77
  async handleHeartbeat(call) {
78
78
  const allowHeartbeat = this.canVoicerHeartbeat(call);
79
+ this.log.debug('handleHeartbeat', { allowHeartbeat });
79
80
  if (allowHeartbeat) {
80
81
  if (this.thread.background) {
81
82
  delete this.waits.timeout;
82
83
  return;
83
84
  }
84
85
  const expectHeartbeatBefore = Date.now() + 290000;
86
+ this.log.debug('expectHeartbeatBefore', { expectHeartbeatBefore });
85
87
  this.triggers.deadline(expectHeartbeatBefore, () => {
86
88
  this.thread.background = true;
87
89
  if (call.ended) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@onereach/step-voice",
3
- "version": "7.0.9-processttschunk.0",
3
+ "version": "7.0.9-processttschunk.10",
4
4
  "author": "Roman Zolotarov <roman.zolotarov@onereach.com>",
5
5
  "contributors": [
6
6
  "Roman Zolotarov",