UNPKG

@onereach/step-voice

Version:
224 lines (223 loc) 9.79 kB
"use strict"; /* eslint-disable @typescript-eslint/strict-boolean-expressions, @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-misused-promises */ Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const voice_1 = tslib_1.__importDefault(require("./voice")); const lodash_1 = tslib_1.__importDefault(require("lodash")); const timestring_1 = tslib_1.__importDefault(require("timestring")); const uuid = tslib_1.__importStar(require("uuid")); const defaultSessionTimeoutMin = 5; class InitiateCall extends voice_1.default { get conversation() { if (this.step.dataOut == null) throw new Error('missing step.dataOut'); return this.step.dataOut; } async runStep() { const { callId = uuid.v4() } = this.data; if (!this.session.key) { const sessionTimeoutMs = (0, timestring_1.default)(`${this.data.sessionTimeout ?? defaultSessionTimeoutMin} min`, 'ms'); await this.session.start({ key: callId, timeout: sessionTimeoutMs, reporting: { settingsKey: 'session', startedBy: 'Bot', sessionType: 'Phone' } }); } const convData = { id: callId, type: 'voicer', vv: 0 }; // vv - voicer version await this.startConversation(convData); // await this.pushConvStep() this.gotoState('waitForCall'); } async onAwake() { const { handleCancel } = this.data; await super.onAwake(); const call = await this.fetchData(); if (!call.ended) throw new Error('unpexpected awake'); if (handleCancel !== true) throw new Error('hangup cancel'); this.exitStep('cancel'); } async waitForCall() { const { asr, tts, from: botNumber, endUserNumber, gatewaySettingsMode, sipHost, sipUser, sipPassword, sipProfile, timeout, headers, enableSpoofCallerId, spoofCallerId, isAMD, otherCallRef, otherCallRefThread, handleCancel, } = this.data; const call = await this.fetchData(); this.triggers.once(`in/voice/${call.id}/event`, async (event) => { switch (event.params.type) { case 'is_flow_ready': { // TODO this.exitFlow({is_ready : true}) should be enough await this.thread.eventManager.callbackAction(this.thread.takeCallback(), async () => { return { result: { is_ready: true } }; }); this.log.debug({ call, conv: this.conversation, data: this.get(this.conversation) }, 'Confirming that the OB call can be started'); lodash_1.default.merge(call, event.params.channel); await this.updateData(); return this.exitFlow(); } case 'call': { const newCall = { headers: event.params.headers, ...event.params.channel, asr: asr.getSettings(), tts: tts.getVoice(), botNumber: event.params.channel.to ?? 'unknown', endUserNumber }; delete newCall.from; delete newCall.to; newCall._conv = call._conv; await this.startConversation(newCall); // await this.popConvStep() await this.transcript(newCall, { action: 'Call Start', reportingSettingsKey: 'transcript', actionFromBot: true }); const commands = [ ...isAMD ? [{ name: 'start-avmd', params: { type: 'avmd_detect' } }] : [], ...asr.serverSettings.enabled ? [{ name: 'start-recognition', params: { type: asr.serverSettings.engine } }] : [], ]; await this.sendCommands(newCall, commands); return this.exitStep('success'); } case 'hangup': { await this.handleHangup(call); if (handleCancel && event.params.error?.name === 'CancelError') { return this.exitStep('cancel'); } return this.throwError(event.params.error ?? { name: 'HangupError', message: 'unpexpected hangup during init call' }); } case 'error': { const error = event.params.error; const errorStatus = error?.originateStatus ?? 'UNKNOWN'; const errorCall = { botNumber, endUserNumber }; switch (errorStatus) { case 'USER_BUSY': await this.transcript(errorCall, { action: 'Call Busy', actionFromBot: true }); await this.updateData(); return this.exitStep('busy', errorCall); case 'NO_ANSWER': case 'NO_USER_RESPONSE': await this.transcript(errorCall, { action: 'Call No Answer', actionFromBot: true }); await this.updateData(); return this.exitStep('no answer', errorCall); default: break; } await this.transcript(errorCall, { action: 'Call Error', actionFromBot: true }); return this.throwError({ name: error?.name ?? 'VoiceError', message: `${String(error?.date)}: ${errorStatus}` }); } default: return this.exitFlow(); } }); this.triggers.otherwise(async () => { await this.triggers.flush(); const originateTimeout = (0, timestring_1.default)(`${timeout ?? 30} sec`, 'ms'); // timeout or 30 seconds const customHeaders = lodash_1.default.reduce(headers, (memo, header) => { memo[header.name] = `${header.value}`; return memo; }, {}); // GET SIP PROFILE SETTINGS let gateway; const profile = sipProfile !== "default" /* SIP_PROFILE.DEFAULT */ ? sipProfile : undefined; switch (gatewaySettingsMode) { case "custom" /* GATEWAY_SETTINGS_MODE.CUSTOM */: gateway = { host: sipHost, user: sipUser, username: sipUser, password: sipPassword, profile }; break; case "profile" /* GATEWAY_SETTINGS_MODE.PROFILE */: gateway = gateway = profile != null ? { profile } : undefined; break; } const params = { id: call.id, from: botNumber, to: endUserNumber, headers: customHeaders, spoofCallerId: { enableSpoofCallerId, spoofCallerId }, timeout: originateTimeout, version: 2, sessionExpireTime: this.session.expireTime, gateway, maxLoops: this.session.data?.loopPrevention?.enabled && this.session.data.loopPrevention.maxLoops, }; if (otherCallRef) { // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const otherThread = this.process.getSafeThread(otherCallRefThread || this.thread.id); const otherCall = await otherThread.get(otherCallRef); if (otherCall == null) throw new Error(`otherCall not found: ${otherCallRef}`); await this.sendCommands(otherCall, [ { name: 'originate', params } ]); } else if (endUserNumber.startsWith('user:')) { await this.thread.emitAsync({ target: 'provider', name: 'out/voice/originate', params }); } else { await this.thread.emitAsync({ target: 'provider', name: 'out/voice/originate/v2', params }); } }); } } exports.default = InitiateCall;