diff --git a/lib/session/call-info.js b/lib/session/call-info.js index 44e73f3d..ac3b813d 100644 --- a/lib/session/call-info.js +++ b/lib/session/call-info.js @@ -12,6 +12,7 @@ class CallInfo { let srf; this.direction = opts.direction; this.traceId = opts.traceId; + this.hasRecording = false; this.callTerminationBy = undefined; if (opts.req) { const u = opts.req.getParsedHeader('from'); diff --git a/lib/session/call-session.js b/lib/session/call-session.js index 34fbe31a..940a3f7e 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -756,69 +756,101 @@ class CallSession extends Emitter { return this._fillerNoise; } - async pauseOrResumeBackgroundListenIfRequired(action, silence = false) { - if ((action == 'pauseCallRecording' || action == 'resumeCallRecording') && - this.backgroundTaskManager.isTaskRunning('record')) { - this.logger.debug({action, silence}, 'CallSession:pauseOrResumeBackgroundListenIfRequired'); - const backgroundListenTask = this.backgroundTaskManager.getTask('record'); - const status = action === 'pauseCallRecording' ? ListenStatus.Pause : ListenStatus.Resume; - backgroundListenTask.updateListen( - status, - silence - ); - } - } async notifyRecordOptions(opts) { - const {action, silence} = opts; + const {action, silence = false, type = 'siprec'} = opts; this.logger.debug({opts}, 'CallSession:notifyRecordOptions'); - this.pauseOrResumeBackgroundListenIfRequired(action, silence); - - /* if we have not answered yet, just save the details for later */ - if (!this.dlg) { - if (action === 'startCallRecording') { - this.recordOptions = opts; - return true; + if (type == 'cloud') { + switch (action) { + case 'pauseCallRecording': + if (this.backgroundTaskManager.isTaskRunning('record')) { + this.logger.debug({action, silence, type}, 'CallSession:cloudRecording'); + const backgroundListenTask = this.backgroundTaskManager.getTask('record'); + backgroundListenTask.updateListen( + ListenStatus.Pause, + silence + ); + return true; + } else { return false; } + case 'resumeCallRecording': + if (this.backgroundTaskManager.isTaskRunning('record')) { + this.logger.debug({action, silence, type}, 'CallSession:cloudRecording'); + const backgroundListenTask = this.backgroundTaskManager.getTask('record'); + backgroundListenTask.updateListen( + ListenStatus.Resume, + silence + ); + return true; + } else { return false; } + case 'startCallRecording': + if (!this.backgroundTaskManager.isTaskRunning('record')) { + this.logger.debug({action, silence, type}, 'CallSession:cloudRecording'); + this.callInfo.hasRecording = true; + this.updateCallStatus(Object.assign({}, this.callInfo.toJSON()), this.serviceUrl) + .catch((err) => this.logger.error(err, 'redis error')); + if (!this.dlg) { + // Call not yet answered so set flag to record on status change + this.application.record_all_calls = true; + } else { + this.backgroundTaskManager.newTask('record'); + } + return true; + } else { return false; } + case 'stopCallRecording': + if (this.backgroundTaskManager.isTaskRunning('record')) { + this.logger.debug({action, silence, type}, 'CallSession:cloudRecording'); + this.backgroundTaskManager.stop('record'); + return true; + } else { return false; } + } + } else { + // SIPREC + /* if we have not answered yet, just save the details for later */ + if (!this.dlg) { + if (action === 'startCallRecording') { + this.recordOptions = opts; + return true; + } + return false; } - return false; - } - /* check validity of request */ - if (action == 'startCallRecording' && this.recordState !== RecordState.RecordingOff) { - this.logger.info({recordState: this.recordState}, - 'CallSession:notifyRecordOptions: recording is already started, ignoring request'); - return false; - } - if (action == 'stopCallRecording' && this.recordState === RecordState.RecordingOff) { - this.logger.info({recordState: this.recordState}, - 'CallSession:notifyRecordOptions: recording is already stopped, ignoring request'); - return false; - } - if (action == 'pauseCallRecording' && this.recordState !== RecordState.RecordingOn) { - this.logger.info({recordState: this.recordState}, - 'CallSession:notifyRecordOptions: cannot pause recording, ignoring request '); - return false; - } - if (action == 'resumeCallRecording' && this.recordState !== RecordState.RecordingPaused) { - this.logger.info({recordState: this.recordState}, - 'CallSession:notifyRecordOptions: cannot resume recording, ignoring request '); - return false; - } + /* check validity of request */ + if (action == 'startCallRecording' && this.recordState !== RecordState.RecordingOff) { + this.logger.info({recordState: this.recordState}, + 'CallSession:notifyRecordOptions: recording is already started, ignoring request'); + return false; + } + if (action == 'stopCallRecording' && this.recordState === RecordState.RecordingOff) { + this.logger.info({recordState: this.recordState}, + 'CallSession:notifyRecordOptions: recording is already stopped, ignoring request'); + return false; + } + if (action == 'pauseCallRecording' && this.recordState !== RecordState.RecordingOn) { + this.logger.info({recordState: this.recordState}, + 'CallSession:notifyRecordOptions: cannot pause recording, ignoring request '); + return false; + } + if (action == 'resumeCallRecording' && this.recordState !== RecordState.RecordingPaused) { + this.logger.info({recordState: this.recordState}, + 'CallSession:notifyRecordOptions: cannot resume recording, ignoring request '); + return false; + } - this.recordOptions = opts; + this.recordOptions = opts; - switch (action) { - case 'startCallRecording': - return await this.startRecording(); - case 'stopCallRecording': - return await this.stopRecording(); - case 'pauseCallRecording': - return await this.pauseRecording(); - case 'resumeCallRecording': - return await this.resumeRecording(); - default: - throw new Error(`invalid record action ${action}`); + switch (action) { + case 'startCallRecording': + return await this.startRecording(); + case 'stopCallRecording': + return await this.stopRecording(); + case 'pauseCallRecording': + return await this.pauseRecording(); + case 'resumeCallRecording': + return await this.resumeRecording(); + default: + throw new Error(`invalid record action ${action}`); + } } } @@ -2980,8 +3012,7 @@ Duration=${duration} ` // manage record all call. if (callStatus === CallStatus.InProgress) { - if (this.accountInfo.account.record_all_calls || - this.application.record_all_calls) { + if (this.accountInfo.account.record_all_calls || this.application.record_all_calls) { this.backgroundTaskManager.newTask('record'); } } else if (callStatus == CallStatus.Completed) { diff --git a/lib/utils/background-task-manager.js b/lib/utils/background-task-manager.js index 7c55b80e..417d838b 100644 --- a/lib/utils/background-task-manager.js +++ b/lib/utils/background-task-manager.js @@ -135,26 +135,24 @@ class BackgroundTaskManager extends Emitter { // Initiate Record async _initRecord() { - if (this.cs.accountInfo.account.record_all_calls || this.cs.application.record_all_calls) { - if (!JAMBONZ_RECORD_WS_BASE_URL || !this.cs.accountInfo.account.bucket_credential) { - this.logger.error('_initRecord: invalid cfg - missing JAMBONZ_RECORD_WS_BASE_URL or bucket config'); - return undefined; - } - const listenOpts = { - url: `${JAMBONZ_RECORD_WS_BASE_URL}/record/${this.cs.accountInfo.account.bucket_credential.vendor}`, - disableBidirectionalAudio: true, - mixType : 'stereo', - passDtmf: true - }; - if (JAMBONZ_RECORD_WS_USERNAME && JAMBONZ_RECORD_WS_PASSWORD) { - listenOpts.wsAuth = { - username: JAMBONZ_RECORD_WS_USERNAME, - password: JAMBONZ_RECORD_WS_PASSWORD - }; - } - this.logger.debug({listenOpts}, '_initRecord: enabling listen'); - return await this._initListen({verb: 'listen', ...listenOpts}, 'jambonz-session-record', true, 'record'); + if (!JAMBONZ_RECORD_WS_BASE_URL || !this.cs.accountInfo.account.bucket_credential) { + this.logger.error('_initRecord: invalid cfg - missing JAMBONZ_RECORD_WS_BASE_URL or bucket config'); + return undefined; } + const listenOpts = { + url: `${JAMBONZ_RECORD_WS_BASE_URL}/record/${this.cs.accountInfo.account.bucket_credential.vendor}`, + disableBidirectionalAudio: true, + mixType : 'stereo', + passDtmf: true + }; + if (JAMBONZ_RECORD_WS_USERNAME && JAMBONZ_RECORD_WS_PASSWORD) { + listenOpts.wsAuth = { + username: JAMBONZ_RECORD_WS_USERNAME, + password: JAMBONZ_RECORD_WS_PASSWORD + }; + } + this.logger.debug({listenOpts}, '_initRecord: enabling listen'); + return await this._initListen({verb: 'listen', ...listenOpts}, 'jambonz-session-record', true, 'record'); } // Initiate Transcribe diff --git a/package-lock.json b/package-lock.json index 67da89ab..8ed962eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "@jambonz/speech-utils": "^0.2.26", "@jambonz/stats-collector": "^0.1.10", "@jambonz/time-series": "^0.2.15", - "@jambonz/verb-specifications": "^0.0.122", + "@jambonz/verb-specifications": "^0.0.123", "@modelcontextprotocol/sdk": "^1.9.0", "@opentelemetry/api": "^1.8.0", "@opentelemetry/exporter-jaeger": "^1.23.0", @@ -1529,9 +1529,9 @@ } }, "node_modules/@jambonz/verb-specifications": { - "version": "0.0.122", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.122.tgz", - "integrity": "sha512-7xqaULhKFywJ2ZuyiYt77iiJwJ+8b98Zt1X4+OqZ7Cdjhfo7S6KnR66XRVJHnekXbmfVv58kB0KWUux5TG//Sw==", + "version": "0.0.123", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.123.tgz", + "integrity": "sha512-yPW8u0Wacz8FKnQpQDjJ2AQHjHTf4S3TlkTyKqFkOyY8lnjnhqg0Mth+9uvOPfJaHgsPd0t9outfQa0wCu5tww==", "license": "MIT", "dependencies": { "debug": "^4.3.4", diff --git a/package.json b/package.json index 42c11c0a..23271f11 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@jambonz/speech-utils": "^0.2.26", "@jambonz/stats-collector": "^0.1.10", "@jambonz/time-series": "^0.2.15", - "@jambonz/verb-specifications": "^0.0.122", + "@jambonz/verb-specifications": "^0.0.123", "@modelcontextprotocol/sdk": "^1.9.0", "@opentelemetry/api": "^1.8.0", "@opentelemetry/exporter-jaeger": "^1.23.0",