diff --git a/lib/session/call-session.js b/lib/session/call-session.js index 69066f38..d9b728fe 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -340,8 +340,8 @@ class CallSession extends Emitter { // this whole thing requires us to be in a Dial verb const task = this.currentTask; - if (!task || TaskName.Dial !== task.name) { - return this.logger.info('CallSession:_lccWhisper - invalid command since we are not in a dial'); + if (!task || ![TaskName.Dial, TaskName.Listen].includes(task.name)) { + return this.logger.info('CallSession:_lccWhisper - invalid command since we are not in a dial or listen'); } // allow user to provide a url object, a url string, an array of tasks, or a single task diff --git a/lib/tasks/dtmf.js b/lib/tasks/dtmf.js new file mode 100644 index 00000000..ea4174c0 --- /dev/null +++ b/lib/tasks/dtmf.js @@ -0,0 +1,41 @@ +const Task = require('./task'); +const {TaskName, TaskPreconditions} = require('../utils/constants'); + +class TaskDtmf extends Task { + constructor(logger, opts) { + super(logger, opts); + this.preconditions = TaskPreconditions.Endpoint; + + this.dtmf = this.data.dtmf; + this.duration = this.data.duration || 500; + } + + get name() { return TaskName.Dtmf; } + + async exec(cs, ep) { + await super.exec(cs); + this.ep = ep; + try { + this.logger.info({data: this.data}, `sending dtmf ${this.dtmf}`); + await this.ep.execute('send_dtmf', `${this.dtmf}@${this.duration}`); + this.timer = setTimeout(this.notifyTaskDone.bind(this), this.dtmf.length * (this.duration + 250) + 750); + await this.awaitTaskDone(); + this.logger.info({data: this.data}, `done sending dtmf ${this.dtmf}`); + } catch (err) { + this.logger.info(err, `TaskDtmf:exec - error playing ${this.dtmf}`); + } + this.emit('playDone'); + } + + async kill(cs) { + super.kill(cs); + if (this.ep.connected && !this.playComplete) { + this.logger.debug('TaskDtmf:kill - killing audio'); + await this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio')); + } + clearTimeout(this.timer); + this.notifyTaskDone(); + } +} + +module.exports = TaskDtmf; diff --git a/lib/tasks/listen.js b/lib/tasks/listen.js index 45c269fb..1c80e146 100644 --- a/lib/tasks/listen.js +++ b/lib/tasks/listen.js @@ -159,6 +159,24 @@ class TaskListen extends Task { this.notifyTaskDone(); } + /** + * play or say something during the call + * @param {*} tasks - array of play/say tasks to execute + */ + async whisper(tasks, callSid) { + try { + const cs = this.callSession; + this.logger.debug('Listen:whisper tasks starting'); + while (tasks.length && !cs.callGone) { + const task = tasks.shift(); + await task.exec(cs, this.ep); + } + this.logger.debug('Listen:whisper tasks complete'); + } catch (err) { + this.logger.error(err, 'Listen:whisper error'); + } + } + } module.exports = TaskListen; diff --git a/lib/tasks/make_task.js b/lib/tasks/make_task.js index a71433ba..089fb389 100644 --- a/lib/tasks/make_task.js +++ b/lib/tasks/make_task.js @@ -30,6 +30,9 @@ function makeTask(logger, obj, parent) { case TaskName.Dequeue: const TaskDequeue = require('./dequeue'); return new TaskDequeue(logger, data, parent); + case TaskName.Dtmf: + const TaskDtmf = require('./dtmf'); + return new TaskDtmf(logger, data, parent); case TaskName.Enqueue: const TaskEnqueue = require('./enqueue'); return new TaskEnqueue(logger, data, parent); diff --git a/lib/tasks/specs.json b/lib/tasks/specs.json index 9b1687c6..6bee22a1 100644 --- a/lib/tasks/specs.json +++ b/lib/tasks/specs.json @@ -141,6 +141,15 @@ "lang" ] }, + "dtmf": { + "properties": { + "dtmf": "string", + "duration": "number" + }, + "required": [ + "dtmf" + ] + }, "lex": { "properties": { "bot": "string", diff --git a/lib/utils/constants.json b/lib/utils/constants.json index 6c6f35b3..737f080f 100644 --- a/lib/utils/constants.json +++ b/lib/utils/constants.json @@ -4,6 +4,7 @@ "Dequeue": "dequeue", "Dial": "dial", "Dialogflow": "dialogflow", + "Dtmf": "dtmf", "Enqueue": "enqueue", "Gather": "gather", "Hangup": "hangup", diff --git a/lib/utils/place-outdial.js b/lib/utils/place-outdial.js index 5afc9c7f..3df693ed 100644 --- a/lib/utils/place-outdial.js +++ b/lib/utils/place-outdial.js @@ -108,11 +108,12 @@ class SingleDialer extends Emitter { this.ep = await ms.createEndpoint(); this.logger.debug(`SingleDialer:exec - created endpoint ${this.ep.uuid}`); - let promiseStreamConnected; + + let lastSdp; const connectStream = async(remoteSdp) => { - // wait for previous re-invite to complete, if any - if (promiseStreamConnected) await promiseStreamConnected.catch((err) => {}); - return promiseStreamConnected = this.ep.modify(remoteSdp); + if (remoteSdp === lastSdp) return; + lastSdp = remoteSdp; + return this.ep.modify(remoteSdp); }; Object.assign(opts, { diff --git a/package-lock.json b/package-lock.json index 1dd3c4a0..2017ddb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -515,9 +515,9 @@ } }, "ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -655,9 +655,9 @@ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" }, "bent": { - "version": "7.3.9", - "resolved": "https://registry.npmjs.org/bent/-/bent-7.3.9.tgz", - "integrity": "sha512-CyuIXgoO6UWJaoMtPHkq6JlzNRjCGQqZQeYuO4W5MxY+P0sO1jM3CMYLf16eoga2ozyhVPzXVUxkPw9rz5VNgA==", + "version": "7.3.12", + "resolved": "https://registry.npmjs.org/bent/-/bent-7.3.12.tgz", + "integrity": "sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==", "requires": { "bytesish": "^0.4.1", "caseless": "~0.12.0", @@ -766,9 +766,9 @@ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, "bytesish": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/bytesish/-/bytesish-0.4.2.tgz", - "integrity": "sha512-ym4cXhq28K7uhYZUEOl17LuqsqKSphDsZcfAKmEa/HcCsCqHMQXOiFuWx1OnbktJux/qKK1W9Xt9uU5kLIKypQ==" + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/bytesish/-/bytesish-0.4.4.tgz", + "integrity": "sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ==" }, "caching-transform": { "version": "4.0.0", @@ -984,11 +984,11 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" } }, "decamelize": { @@ -1151,9 +1151,9 @@ "integrity": "sha1-Bmlv43Wvt/68AI8oaCJWukEHWAE=" }, "drachtio-sip": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.4.0.tgz", - "integrity": "sha512-/HHK0KH8sfQxUuVrYwaaRxdCq7j6sAQMxzegqzGQeOULx2TNeYb9Bf2SF5AO59DpmYkQRc+ezuHX58EpN/SR7A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.4.1.tgz", + "integrity": "sha512-XjrsVb4O5bjgAr/hWogFk8TLoSp2bbOKYhFAFRayiMs/w7PkYep5z5IOW+erbR8LgVDjZVfdGxjA0RPkI4QMvA==", "requires": { "debug": "^4.1.1", "merge": ">=1.2.1", @@ -1163,16 +1163,16 @@ } }, "drachtio-srf": { - "version": "4.4.37", - "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.37.tgz", - "integrity": "sha512-Z7MpHzee8z6gYVFgLRCqTC2Tt7d0v+EjDLhpZDLpEX0gMTs2Qey+VZ6QxO4CGyMrysZsLBZG5ZBNEifC2gBkHg==", + "version": "4.4.39", + "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.39.tgz", + "integrity": "sha512-wqiCe3nM8C9OJwllYVTMoYpVIjtWgX1TcyYy4y0b8jII9uhyB6CaK8aRfgzesZLJ6NTVlioS3a2Ic3e3Cmrkng==", "requires": { "async": "^1.4.2", "debug": "^3.2.6", "delegates": "^0.1.0", "deprecate": "^1.1.1", "drachtio-mw-registration-parser": "0.0.2", - "drachtio-sip": "^0.4.0", + "drachtio-sip": "^0.4.1", "lodash": "^4.17.20", "node-noop": "0.0.1", "only": "0.0.2", @@ -1319,9 +1319,9 @@ "dev": true }, "eslint": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.10.0.tgz", - "integrity": "sha512-BDVffmqWl7JJXqCjAK6lWtcQThZB/aP1HXSH1JKwGwv0LQEdvpR7qzNrUT487RM39B5goWuboFad5ovMBmD8yA==", + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.11.0.tgz", + "integrity": "sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -1334,7 +1334,7 @@ "enquirer": "^2.3.5", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^1.3.0", + "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.0", "esquery": "^1.2.0", "esutils": "^2.0.2", @@ -1400,12 +1400,20 @@ "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", "dev": true }, "espree": { @@ -1417,6 +1425,14 @@ "acorn": "^7.4.0", "acorn-jsx": "^5.2.0", "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "esprima": { @@ -1587,9 +1603,9 @@ "dev": true }, "fast-redact": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-2.0.0.tgz", - "integrity": "sha512-zxpkULI9W9MNTK2sJ3BpPQrTEXFNESd2X6O1tXMFpK/XM0G5c5Rll2EVYZH2TqI3xRGK/VaJ+eEOt7pnENJpeA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.0.tgz", + "integrity": "sha512-a/S/Hp6aoIjx7EmugtzLqXmcNsyFszqbt6qQ99BdG61QjBZF6shNis0BYR6TsZOQ1twYc0FN2Xdhwwbv6+KD0w==" }, "fast-safe-stringify": { "version": "2.0.7", @@ -2529,9 +2545,9 @@ } }, "moment": { - "version": "2.27.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz", - "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "ms": { "version": "2.1.2", @@ -2861,11 +2877,11 @@ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, "pino": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-6.5.1.tgz", - "integrity": "sha512-76+RUhQkqjUD4AtQcSfEzh6vlsjXmoWZK5gg+2d70aCLXZTbo4/5js4I9rN1Xk6z1h2/7pnOFX10G4c2T4qNiA==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-6.7.0.tgz", + "integrity": "sha512-vPXJ4P9rWCwzlTJt+f0Ni4THc3DWyt8iDDCO4edQ8narTu6hnpzdXu8FqeSJCGndl1W6lfbYQUQihUO54y66Lw==", "requires": { - "fast-redact": "^2.0.0", + "fast-redact": "^3.0.0", "fast-safe-stringify": "^2.0.7", "flatstr": "^1.0.12", "pino-std-serializers": "^2.4.2", @@ -3289,9 +3305,9 @@ } }, "sonic-boom": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.1.0.tgz", - "integrity": "sha512-JyOf+Xt7GBN4tAic/DD1Bitw6OMgSHAnswhPeOiLpfRoSjPNjEIi73UF3OxHzhSNn9WavxGuCZzprFCGFSNwog==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.0.tgz", + "integrity": "sha512-4nX6OYvOYr6R76xfQKi6cZpTO3YSWe/vd+QdIfoH0lBy0MnPkeAbb2rRWgmgADkXUeCKPwO1FZAKlAVWAadELw==", "requires": { "atomic-sleep": "^1.0.0", "flatstr": "^1.0.12" diff --git a/package.json b/package.json index 53be86f3..9a0bf4b2 100644 --- a/package.json +++ b/package.json @@ -29,25 +29,25 @@ "@jambonz/db-helpers": "^0.5.1", "@jambonz/realtimedb-helpers": "^0.2.17", "@jambonz/stats-collector": "^0.0.4", - "bent": "^7.3.9", + "bent": "^7.3.12", "cidr-matcher": "^2.1.1", - "debug": "^4.1.1", + "debug": "^4.2.0", "deepcopy": "^2.1.0", "drachtio-fsmrf": "^2.0.2", - "drachtio-srf": "^4.4.37", + "drachtio-srf": "^4.4.39", "express": "^4.17.1", "ip": "^1.1.5", "jambonz-mw-registrar": "^0.1.3", - "moment": "^2.27.0", + "moment": "^2.29.1", "parse-url": "^5.0.2", - "pino": "^6.5.1", + "pino": "^6.7.0", "verify-aws-sns-signature": "^0.0.6", "xml2js": "^0.4.23" }, "devDependencies": { "blue-tape": "^1.0.0", "clear-module": "^4.1.1", - "eslint": "^7.10.0", + "eslint": "^7.11.0", "eslint-plugin-promise": "^4.2.1", "nyc": "^15.1.0", "tap-dot": "^2.0.0",