snake case REST payloads, support for LCC with child_call_hook, handle 302 on outdial

This commit is contained in:
Dave Horton
2021-04-22 14:39:54 -04:00
parent 8eb0cd1520
commit 576f645489
11 changed files with 340 additions and 179 deletions

View File

@@ -7,11 +7,13 @@ const {DbErrorUnprocessableRequest} = require('../utils/errors');
/**
* validate the call state
*/
function retrieveCallSession(callSid, opts) {
function retrieveCallSession(logger, callSid, opts) {
logger.debug(`retrieving session for callSid ${callSid}`);
const cs = sessionTracker.get(callSid);
if (cs) {
const task = cs.currentTask;
if (!task || task.name != TaskName.Enqueue) {
logger.debug({cs}, 'found call session but not in Enqueue task??');
throw new DbErrorUnprocessableRequest(`enqueue api failure: indicated call is not queued: ${task.name}`);
}
}
@@ -19,14 +21,14 @@ function retrieveCallSession(callSid, opts) {
}
/**
* notify a waiting session that a conference has started
* notify a waiting session that a queue event has occurred
*/
router.post('/:callSid', async(req, res) => {
const logger = req.app.locals.logger;
const callSid = req.params.callSid;
logger.debug({body: req.body}, 'got enqueue event');
logger.debug({callSid, body: req.body}, 'got enqueue event');
try {
const cs = retrieveCallSession(callSid, req.body);
const cs = retrieveCallSession(logger, callSid, req.body);
if (!cs) {
logger.info(`enqueue: callSid not found ${callSid}`);
return res.sendStatus(404);

View File

@@ -0,0 +1,43 @@
const CallSession = require('./call-session');
/**
* @classdesc Subclass of CallSession. Represents a CallSession
* that was initially a child call leg; i.e. established via a Dial verb.
* Now it is all grown up and filling out its own CallSession. Yoo-hoo!
* @extends CallSession
*/
class AdultingCallSession extends CallSession {
constructor({logger, application, singleDialer, tasks, callInfo}) {
super({
logger,
application,
srf: singleDialer.dlg.srf,
tasks,
callInfo
});
this.sd = singleDialer;
this.sd.dlg.on('destroy', () => {
this.logger.info('AdultingCallSession: called party hung up');
this._callReleased();
});
this.sd.emit('adulting');
}
get dlg() {
return this.sd.dlg;
}
get ep() {
return this.sd.ep;
}
get callSid() {
return this.callInfo.callSid;
}
}
module.exports = AdultingCallSession;

View File

@@ -45,7 +45,7 @@ class CallSession extends Emitter {
this.serviceUrl = srf.locals.serviceUrl;
}
if (!this.isConfirmCallSession && !this.isSmsCallSession) {
if (!this.isConfirmCallSession && !this.isSmsCallSession && !this.isAdultingCallSession) {
sessionTracker.add(this.callSid, this);
}
}
@@ -161,6 +161,12 @@ class CallSession extends Emitter {
return this.application.transferredCall === true;
}
/**
* returns true if this session is a ConfirmCallSession
*/
get isAdultingCallSession() {
return this.constructor.name === 'AdultingCallSession';
}
/**
* returns true if this session is a ConfirmCallSession
*/
@@ -296,10 +302,47 @@ class CallSession extends Emitter {
* @param {object} [opts.call_hook] - new call_status_hook
*/
async _lccCallHook(opts) {
const tasks = await this.requestor.request(opts.call_hook, this.callInfo);
if (tasks && tasks.length > 0) {
this.logger.info({tasks: listTaskNames(tasks)}, 'CallSession:updateCall new task list');
this.replaceApplication(normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata)));
const webhooks = [];
let sd;
if (opts.call_hook) webhooks.push(this.requestor.request(opts.call_hook, this.callInfo));
if (opts.child_call_hook) {
/* child call hook only allowed from a connected Dial state */
const task = this.currentTask;
sd = task.sd;
if (task && TaskName.Dial === task.name && sd) {
webhooks.push(this.requestor.request(opts.child_call_hook, sd.callInfo));
}
}
const [tasks1, tasks2] = await Promise.all(webhooks);
let tasks, childTasks;
if (opts.call_hook) {
tasks = tasks1;
if (opts.child_call_hook) childTasks = tasks2;
}
else childTasks = tasks1;
if (childTasks) {
const {parentLogger} = this.srf.locals;
const childLogger = parentLogger.child({callId: this.callId, callSid: sd.callSid});
const t = normalizeJambones(childLogger, childTasks).map((tdata) => makeTask(childLogger, tdata));
childLogger.info({tasks: listTaskNames(t)}, 'CallSession:updateCall new task list for child call');
const cs = await sd.doAdulting({
logger: childLogger,
application: this.application,
tasks: t
});
/* need to update the callSid of the child with its own (new) AdultingCallSession */
sessionTracker.add(cs.callSid, cs);
}
if (tasks) {
const t = normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata));
this.logger.info({tasks: listTaskNames(t)}, 'CallSession:updateCall new task list');
this.replaceApplication(t);
}
else {
/* we started a new app on the child leg, but nothing given for parent so hang him up */
this.currentTask.kill();
}
}
@@ -404,7 +447,7 @@ class CallSession extends Emitter {
if (opts.call_status) {
return this._lccCallStatus(opts);
}
if (opts.call_hook) {
if (opts.call_hook || opts.child_call_hook) {
return await this._lccCallHook(opts);
}
if (opts.listen_status) {
@@ -489,9 +532,16 @@ class CallSession extends Emitter {
const ep = await this.ms.createEndpoint({remoteSdp: this.req.body});
ep.cs = this;
this.ep = ep;
await ep.set('hangup_after_bridge', false);
ep.set({
hangup_after_bridge: false,
park_after_bridge: true
}).catch((err) => this.logger.error({err}, 'Error setting park_after_bridge'));
this.logger.debug('allocated endpoint');
this.logger.debug(`allocated endpoint ${this.ep.uuid}`);
this.ep.on('destroy', () => {
this.logger.info(`endpoint was destroyed!! ${this.ep.uuid}`);
});
if (this.direction === CallDirection.Inbound) {
if (task.earlyMedia && !this.req.finalResponseSent) {

View File

@@ -113,6 +113,11 @@ class TaskDial extends Task {
}
get ep() {
/**
* Note:
* this.ep is the B leg-facing endpoint
* this.epOther is the A leg-facing endpoint
*/
if (this.sd) return this.sd.ep;
}
@@ -133,6 +138,8 @@ class TaskDial extends Task {
this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector);
await this._attemptCalls(cs);
await this.awaitTaskDone();
this.logger.debug({callSid: this.cs.callSid}, 'Dial:exec task is done, sending actionHook if any');
await this.performAction(this.results);
this._removeDtmfDetection(cs, this.epOther);
this._removeDtmfDetection(cs, this.ep);
@@ -146,6 +153,8 @@ class TaskDial extends Task {
super.kill(cs);
this._removeDtmfDetection(this.cs, this.epOther);
this._removeDtmfDetection(this.cs, this.ep);
this.logger.debug({callSid: this.cs.callSid}, 'Dial:kill removed dtmf listeners');
this._killOutdials();
if (this.sd) {
this.sd.kill();
@@ -212,6 +221,7 @@ class TaskDial extends Task {
_removeDtmfDetection(cs, ep) {
if (ep) {
delete ep.dtmfDetector;
this.logger.debug(`Dial:_removeDtmfDetection endpoint ${ep.uuid}`);
ep.removeAllListeners('dtmf');
}
}
@@ -219,16 +229,21 @@ class TaskDial extends Task {
_onDtmf(cs, ep, evt) {
if (ep.dtmfDetector) {
const match = ep.dtmfDetector.keyPress(evt.dtmf);
if (match) {
this.logger.debug({callSid: this.cs.callSid}, `Dial:_onDtmf triggered dtmf match: ${match}`);
const requestor = ep.dtmfDetector === this.parentDtmfCollector ?
cs.requestor :
this.sd.requestor;
if (match) {
this.logger.debug(`parentCall triggered dtmf match: ${match}`);
(this.sd ? this.sd.requestor : null);
if (!requestor) {
this.logger.info(`Dial:_onDtmf got digits on B leg after adulting: ${evt.dtmf}`);
}
else {
requestor.request(this.dtmfHook, Object.assign({dtmf: match}, cs.callInfo))
.catch((err) => this.logger.info(err, 'Dial:_onDtmf - error'));
}
}
}
}
async _initializeInbound(cs) {
const ep = await cs._evalEndpointPrecondition(this);
@@ -351,6 +366,16 @@ class TaskDial extends Task {
this.logger.debug('Dial:_attemptCalls - all calls failed after decline, ending task');
this.kill(cs);
}
})
.once('adulting', () => {
/* child call just adulted and got its own session */
this.logger.info('Dial:on_adulting: detaching child call leg');
if (this.ep) {
this.logger.debug(`Dial:on_adulting: removing dtmf from ${this.ep.uuid}`);
this.ep.removeAllListeners('dtmf');
}
this.sd = null;
this.callSid = null;
});
} catch (err) {
this.logger.error(err, 'Dial:_attemptCalls');
@@ -399,11 +424,14 @@ class TaskDial extends Task {
}
sessionTracker.add(this.callSid, cs);
this.dlg.on('destroy', () => {
/* if our child is adulting, he's own his own now.. */
if (this.dlg) {
this.logger.debug('Dial:_selectSingleDial called party hungup, ending dial operation');
sessionTracker.remove(this.callSid);
if (this.timerMaxCallDuration) clearTimeout(this.timerMaxCallDuration);
this.ep.unbridge();
this.kill(cs);
}
});
Object.assign(this.results, {

View File

@@ -275,18 +275,19 @@ class TaskEnqueue extends Task {
this.logger.error({err}, `TaskEnqueue:_playHook error retrieving list info for queue ${this.queueName}`);
}
const json = await cs.application.requestor.request(hook, params);
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
const allowedTasks = json.filter((task) => allowed.includes(task.verb));
if (json.length !== allowedTasks.length) {
this.logger.debug({json, allowedTasks}, 'unsupported task');
throw new Error(`unsupported verb in dial enqueue waitHook: only ${JSON.stringify(allowed)}`);
const allowedTasks = tasks.filter((t) => allowed.includes(t.verb));
if (tasks.length !== allowedTasks.length) {
this.logger.debug({tasks, allowedTasks}, 'unsupported task');
throw new Error(`unsupported verb in enqueue waitHook: only ${JSON.stringify(allowed)}`);
}
this.logger.debug(`TaskEnqueue:_playHook: executing ${json.length} tasks`);
this.logger.debug(`TaskEnqueue:_playHook: executing ${tasks.length} tasks`);
// check for 'leave' verb and only execute tasks up till then
const tasksToRun = [];
let leave = false;
for (const o of json) {
for (const o of tasks) {
if (o.verb === TaskName.Leave) {
leave = true;
this.logger.info('waitHook returned a leave task');
@@ -297,14 +298,13 @@ class TaskEnqueue extends Task {
if (this.killed) return [];
else if (tasksToRun.length > 0) {
const tasks = normalizeJambones(this.logger, tasksToRun).map((tdata) => makeTask(this.logger, tdata));
this._playSession = new ConfirmCallSession({
logger: this.logger,
application: cs.application,
dlg,
ep: cs.ep,
callInfo: cs.callInfo,
tasks
tasksToRun
});
await this._playSession.exec();
this._playSession = null;

View File

@@ -91,6 +91,7 @@ class TaskGather extends Task {
kill(cs) {
super.kill(cs);
this._killAudio();
this.ep.removeAllListeners('dtmf');
this._resolve('killed');
}

View File

@@ -34,11 +34,13 @@ class TaskSay extends Task {
})))
.filter((fp) => fp && fp.length);
this.logger.debug({files}, 'synthesized files for tts');
this.logger.debug({files, loop: this.loop}, 'synthesized files for tts');
if (!this.ep.connected) this.logger.debug('say: endpoint is not connected!');
while (!this.killed && this.loop-- && this.ep.connected) {
let segment = 0;
do {
this.logger.debug(`playing file ${files[segment]}`);
await ep.play(files[segment]);
} while (!this.killed && ++segment < files.length);
}

View File

@@ -7,6 +7,7 @@ const assert = require('assert');
const ConfirmCallSession = require('../session/confirm-call-session');
const selectSbc = require('./select-sbc');
const Registrar = require('@jambonz/mw-registrar');
const AdultingCallSession = require('../session/adulting-call-session');
const registrar = new Registrar({
host: process.env.JAMBONES_REDIS_HOST,
port: process.env.JAMBONES_REDIS_PORT || 6379
@@ -137,7 +138,7 @@ class SingleDialer extends Emitter {
localSdp: this.ep.local.sdp
});
if (this.target.auth) opts.auth = this.target.auth;
this.dlg = await srf.createUAC(uri, opts, {
this.dlg = await srf.createUAC(uri, {...opts, followRedirects: true, keepUriOnRedirect: true}, {
cbRequest: (err, req) => {
if (err) {
this.logger.error(err, 'SingleDialer:exec Error creating call');
@@ -266,7 +267,7 @@ class SingleDialer extends Emitter {
// now execute it in a new ConfirmCallSession
this.logger.debug(`SingleDialer:_executeApp: executing ${tasks.length} tasks`);
const cs = new ConfirmCallSession({
logger: this.logger,
logger: this.baseLogger,
application: this.application,
dlg: this.dlg,
ep: this.ep,
@@ -284,6 +285,24 @@ class SingleDialer extends Emitter {
}
}
async doAdulting({logger, tasks, application}) {
this.logger = logger;
this.adulting = true;
this.emit('adulting');
await this.ep.unbridge()
.catch((err) => this.logger.info({err}, 'SingleDialer:doAdulting - failed to unbridge ep'));
this.ep.play('silence_stream://1000');
const cs = new AdultingCallSession({
logger: this.logger,
singleDialer: this,
application,
callInfo: this.callInfo,
tasks
});
cs.exec();
return cs;
}
_notifyCallStatusChange({callStatus, sipStatus, duration}) {
assert((typeof duration === 'number' && callStatus === CallStatus.Completed) ||
(!duration && callStatus !== CallStatus.Completed),

View File

@@ -1,7 +1,7 @@
const bent = require('bent');
const parseUrl = require('parse-url');
const assert = require('assert');
const snakeCaseKeys = require('snakecase-keys');
const toBase64 = (str) => Buffer.from(str || '', 'utf8').toString('base64');
function basicAuth(username, password) {
@@ -62,7 +62,7 @@ class Requestor {
* @param {object} [params] - request parameters
*/
async request(hook, params) {
params = params || null;
const payload = params ? snakeCaseKeys(params, {exclude: ['customerData', 'sip']}) : null;
const url = hook.url || hook;
const method = hook.method || 'POST';
const {username, password} = typeof hook === 'object' ? hook : {};
@@ -70,14 +70,14 @@ class Requestor {
assert.ok(url, 'Requestor:request url was not provided');
assert.ok, (['GET', 'POST'].includes(method), `Requestor:request method must be 'GET' or 'POST' not ${method}`);
this.logger.debug({hook, params}, `Requestor:request ${method} ${url}`);
this.logger.debug({hook, payload}, `Requestor:request ${method} ${url}`);
const startAt = process.hrtime();
let buf;
try {
buf = isRelativeUrl(url) ?
await this.post(url, params, this.authHeader) :
await bent(method, 'buffer', 200, 201, 202)(url, params, basicAuth(username, password));
await this.post(url, payload, this.authHeader) :
await bent(method, 'buffer', 200, 201, 202)(url, payload, basicAuth(username, password));
} catch (err) {
this.logger.info({baseUrl: this.baseUrl, url, statusCode: err.statusCode},
`web callback returned unexpected error code ${err.statusCode}`);

289
package-lock.json generated
View File

@@ -8,9 +8,9 @@
"version": "0.3.0",
"license": "MIT",
"dependencies": {
"@jambonz/db-helpers": "^0.5.10",
"@jambonz/db-helpers": "^0.5.20",
"@jambonz/mw-registrar": "^0.1.9",
"@jambonz/realtimedb-helpers": "^0.4.0",
"@jambonz/realtimedb-helpers": "^0.4.1",
"@jambonz/stats-collector": "^0.1.5",
"aws-sdk": "^2.848.0",
"bent": "^7.3.12",
@@ -18,12 +18,13 @@
"debug": "^4.3.1",
"deepcopy": "^2.1.0",
"drachtio-fsmrf": "^2.0.7",
"drachtio-srf": "^4.4.47",
"drachtio-srf": "^4.4.48",
"express": "^4.17.1",
"ip": "^1.1.5",
"moment": "^2.29.1",
"parse-url": "^5.0.2",
"pino": "^6.11.1",
"snakecase-keys": "^3.2.1",
"uuid": "^8.3.2",
"verify-aws-sns-signature": "^0.0.6",
"xml2js": "^0.4.23"
@@ -400,10 +401,11 @@
}
},
"node_modules/@jambonz/db-helpers": {
"version": "0.5.11",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.11.tgz",
"integrity": "sha512-3IV28+SD07AFPAf4a9BMjbBlPc4LOQ62uNdVO4hFY4rG7ttj/eWRYoKFItI0564MbSTHXtNyx1DsqHpI+60ZwA==",
"version": "0.5.20",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.20.tgz",
"integrity": "sha512-oMwujBu+twaSJxdAOnU9a6yhIZHLyAg/EwNbOjPUxofOPKOXV4Wma8ZtyWhWBvfdBLZfngE5pqIVkG6Uen/BTA==",
"dependencies": {
"cidr-matcher": "^2.1.1",
"debug": "^4.3.1",
"mysql2": "^2.2.5",
"uuid": "^8.3.2"
@@ -419,31 +421,6 @@
"redis": "^3.0.2"
}
},
"node_modules/@jambonz/mw-registrar/node_modules/redis": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz",
"integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==",
"dependencies": {
"denque": "^1.4.1",
"redis-commands": "^1.5.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@jambonz/mw-registrar/node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@jambonz/promisify-redis": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@jambonz/promisify-redis/-/promisify-redis-0.0.6.tgz",
@@ -452,22 +429,17 @@
"redis-commands": "^1.6.0"
}
},
"node_modules/@jambonz/promisify-redis/node_modules/redis-commands": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"node_modules/@jambonz/realtimedb-helpers": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.0.tgz",
"integrity": "sha512-MA+JFC6oV1DpgxtSgoZBMVCz5EJkSOxjV6c6OZRXv219gvMFwbvbMPnh4f7Fhry8jbwKA4CMl5HXwmY5fp7iEQ==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.1.tgz",
"integrity": "sha512-GDZ+VkVkSatAh6rYxapzi3r0k/Lj45mh1LAV6aB8U1kTcm3cD9u8QDBB04JL+seBg9mZmEB5aEiCdE9icOnJew==",
"dependencies": {
"@google-cloud/text-to-speech": "^3.1.3",
"@jambonz/promisify-redis": "0.0.6",
"@jambonz/stats-collector": "^0.1.5",
"aws-sdk": "^2.840.0",
"debug": "^4.3.1",
"redis": "^2.8.0"
"redis": "^3.0.0"
}
},
"node_modules/@jambonz/stats-collector": {
@@ -1403,11 +1375,6 @@
"ignored": "bin/ignored"
}
},
"node_modules/double-ended-queue": {
"version": "2.1.0-0",
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
"integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
},
"node_modules/drachtio-fsmrf": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-2.0.7.tgz",
@@ -1482,26 +1449,26 @@
}
},
"node_modules/drachtio-sip": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.2.tgz",
"integrity": "sha512-vwHRQQuS12JKO5hFdZKD8cYPJKQA1EFd5KVpw1ZuVSizMLrxz3++CuWYwRMmH97vLVWG4FMGFWjlDO3S2fhtPw==",
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.3.tgz",
"integrity": "sha512-Ja4ToevqYCQj/8rIM7oEDfncypMxVVdvw1Zo471otfAxCXEzMokbVQGlADcp3lpFEmuWCo1ypE6hItVKe2z0dg==",
"dependencies": {
"debug": "^4.3.1",
"only": "0.0.2",
"sip-message-examples": "0.0.5",
"sip-status": "~0.1.0"
}
},
"node_modules/drachtio-srf": {
"version": "4.4.47",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.47.tgz",
"integrity": "sha512-dpbutyaRWj0llqRShkd02nsaLj0dlSaKJhG6IzxeLQSiyY6h5/vtqEDaw/uft/sSqwrMWwu+6SlkutKjb5ei0A==",
"version": "4.4.48",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.48.tgz",
"integrity": "sha512-+ocXNV7pVh7yntNSuC96mFp3uuCYxGBz05G+M6ik0DztLy6Ri5J9zA5JbOWAtSB8XIfsw0xxdPLQjJtlZq8D5Q==",
"license": "MIT",
"dependencies": {
"async": "^1.4.2",
"debug": "^3.2.7",
"delegates": "^0.1.0",
"deprecate": "^1.1.1",
"drachtio-sip": "^0.5.2",
"drachtio-sip": "^0.5.3",
"node-noop": "0.0.1",
"only": "0.0.2",
"sdp-transform": "^2.14.1",
@@ -3233,6 +3200,17 @@
"node": ">=8"
}
},
"node_modules/map-obj": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz",
"integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==",
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -3927,22 +3905,27 @@
"dev": true
},
"node_modules/redis": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
"integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
"dependencies": {
"double-ended-queue": "^2.1.0-0",
"redis-commands": "^1.2.0",
"redis-parser": "^2.6.0"
"denque": "^1.5.0",
"redis-commands": "^1.7.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
},
"engines": {
"node": ">=0.10.0"
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-redis"
}
},
"node_modules/redis-commands": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz",
"integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg=="
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"node_modules/redis-errors": {
"version": "1.2.0",
@@ -3953,11 +3936,14 @@
}
},
"node_modules/redis-parser": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
"integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=0.10.0"
"node": ">=4"
}
},
"node_modules/regexp.prototype.flags": {
@@ -4229,11 +4215,6 @@
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true
},
"node_modules/sip-message-examples": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/sip-message-examples/-/sip-message-examples-0.0.5.tgz",
"integrity": "sha512-p/t6su7UkiicfUR2CWlUO3rJJIeGO+6oekdnCp8QC6ut5z3AyfhjaefmJKWW8JyrC/2nZYmLdEfBqpKryS4QSg=="
},
"node_modules/sip-methods": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/sip-methods/-/sip-methods-0.3.0.tgz",
@@ -4288,6 +4269,18 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/snakecase-keys": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz",
"integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==",
"dependencies": {
"map-obj": "^4.1.0",
"to-snake-case": "^1.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/sonic-boom": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.2.tgz",
@@ -4737,6 +4730,27 @@
"node": ">=4"
}
},
"node_modules/to-no-case": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
"integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo="
},
"node_modules/to-snake-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz",
"integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=",
"dependencies": {
"to-space-case": "^1.0.0"
}
},
"node_modules/to-space-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
"integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=",
"dependencies": {
"to-no-case": "^1.0.0"
}
},
"node_modules/toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
@@ -5462,10 +5476,11 @@
"dev": true
},
"@jambonz/db-helpers": {
"version": "0.5.11",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.11.tgz",
"integrity": "sha512-3IV28+SD07AFPAf4a9BMjbBlPc4LOQ62uNdVO4hFY4rG7ttj/eWRYoKFItI0564MbSTHXtNyx1DsqHpI+60ZwA==",
"version": "0.5.20",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.5.20.tgz",
"integrity": "sha512-oMwujBu+twaSJxdAOnU9a6yhIZHLyAg/EwNbOjPUxofOPKOXV4Wma8ZtyWhWBvfdBLZfngE5pqIVkG6Uen/BTA==",
"requires": {
"cidr-matcher": "^2.1.1",
"debug": "^4.3.1",
"mysql2": "^2.2.5",
"uuid": "^8.3.2"
@@ -5479,27 +5494,6 @@
"@jambonz/promisify-redis": "0.0.6",
"debug": "^4.3.1",
"redis": "^3.0.2"
},
"dependencies": {
"redis": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz",
"integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==",
"requires": {
"denque": "^1.4.1",
"redis-commands": "^1.5.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
}
},
"redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
"requires": {
"redis-errors": "^1.0.0"
}
}
}
},
"@jambonz/promisify-redis": {
@@ -5508,26 +5502,19 @@
"integrity": "sha512-9KmWV+ODDOPwdqijhgXOXkloGNm7nmCf3ch4D1vN46lh9FQLmnlQmEmBHIFaKB3vAgIgcMzNzcNmY23JGCGguA==",
"requires": {
"redis-commands": "^1.6.0"
},
"dependencies": {
"redis-commands": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
}
}
},
"@jambonz/realtimedb-helpers": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.0.tgz",
"integrity": "sha512-MA+JFC6oV1DpgxtSgoZBMVCz5EJkSOxjV6c6OZRXv219gvMFwbvbMPnh4f7Fhry8jbwKA4CMl5HXwmY5fp7iEQ==",
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.4.1.tgz",
"integrity": "sha512-GDZ+VkVkSatAh6rYxapzi3r0k/Lj45mh1LAV6aB8U1kTcm3cD9u8QDBB04JL+seBg9mZmEB5aEiCdE9icOnJew==",
"requires": {
"@google-cloud/text-to-speech": "^3.1.3",
"@jambonz/promisify-redis": "0.0.6",
"@jambonz/stats-collector": "^0.1.5",
"aws-sdk": "^2.840.0",
"debug": "^4.3.1",
"redis": "^2.8.0"
"redis": "^3.0.0"
}
},
"@jambonz/stats-collector": {
@@ -6335,11 +6322,6 @@
"minimatch": "^3.0.4"
}
},
"double-ended-queue": {
"version": "2.1.0-0",
"resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz",
"integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw="
},
"drachtio-fsmrf": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-2.0.7.tgz",
@@ -6406,26 +6388,25 @@
}
},
"drachtio-sip": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.2.tgz",
"integrity": "sha512-vwHRQQuS12JKO5hFdZKD8cYPJKQA1EFd5KVpw1ZuVSizMLrxz3++CuWYwRMmH97vLVWG4FMGFWjlDO3S2fhtPw==",
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/drachtio-sip/-/drachtio-sip-0.5.3.tgz",
"integrity": "sha512-Ja4ToevqYCQj/8rIM7oEDfncypMxVVdvw1Zo471otfAxCXEzMokbVQGlADcp3lpFEmuWCo1ypE6hItVKe2z0dg==",
"requires": {
"debug": "^4.3.1",
"only": "0.0.2",
"sip-message-examples": "0.0.5",
"sip-status": "~0.1.0"
}
},
"drachtio-srf": {
"version": "4.4.47",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.47.tgz",
"integrity": "sha512-dpbutyaRWj0llqRShkd02nsaLj0dlSaKJhG6IzxeLQSiyY6h5/vtqEDaw/uft/sSqwrMWwu+6SlkutKjb5ei0A==",
"version": "4.4.48",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.4.48.tgz",
"integrity": "sha512-+ocXNV7pVh7yntNSuC96mFp3uuCYxGBz05G+M6ik0DztLy6Ri5J9zA5JbOWAtSB8XIfsw0xxdPLQjJtlZq8D5Q==",
"requires": {
"async": "^1.4.2",
"debug": "^3.2.7",
"delegates": "^0.1.0",
"deprecate": "^1.1.1",
"drachtio-sip": "^0.5.2",
"drachtio-sip": "^0.5.3",
"node-noop": "0.0.1",
"only": "0.0.2",
"sdp-transform": "^2.14.1",
@@ -7846,6 +7827,11 @@
"semver": "^6.0.0"
}
},
"map-obj": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.2.1.tgz",
"integrity": "sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ=="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -8405,19 +8391,20 @@
}
},
"redis": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz",
"integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==",
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
"requires": {
"double-ended-queue": "^2.1.0-0",
"redis-commands": "^1.2.0",
"redis-parser": "^2.6.0"
"denque": "^1.5.0",
"redis-commands": "^1.7.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
}
},
"redis-commands": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz",
"integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg=="
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"redis-errors": {
"version": "1.2.0",
@@ -8425,9 +8412,12 @@
"integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
},
"redis-parser": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz",
"integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
"requires": {
"redis-errors": "^1.0.0"
}
},
"regexp.prototype.flags": {
"version": "1.3.1",
@@ -8654,11 +8644,6 @@
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
"dev": true
},
"sip-message-examples": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/sip-message-examples/-/sip-message-examples-0.0.5.tgz",
"integrity": "sha512-p/t6su7UkiicfUR2CWlUO3rJJIeGO+6oekdnCp8QC6ut5z3AyfhjaefmJKWW8JyrC/2nZYmLdEfBqpKryS4QSg=="
},
"sip-methods": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/sip-methods/-/sip-methods-0.3.0.tgz",
@@ -8706,6 +8691,15 @@
}
}
},
"snakecase-keys": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz",
"integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==",
"requires": {
"map-obj": "^4.1.0",
"to-snake-case": "^1.0.0"
}
},
"sonic-boom": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.3.2.tgz",
@@ -9094,6 +9088,27 @@
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
},
"to-no-case": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz",
"integrity": "sha1-xyKQcWTvaxeBMsjmmTAhLRtKoWo="
},
"to-snake-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz",
"integrity": "sha1-znRpE4l5RgGah+Yu366upMYIq4w=",
"requires": {
"to-space-case": "^1.0.0"
}
},
"to-space-case": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz",
"integrity": "sha1-sFLar7Gysp3HcM6gFj5ewOvJ/Bc=",
"requires": {
"to-no-case": "^1.0.0"
}
},
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",

View File

@@ -26,9 +26,9 @@
"jslint": "eslint app.js lib"
},
"dependencies": {
"@jambonz/db-helpers": "^0.5.10",
"@jambonz/db-helpers": "^0.5.20",
"@jambonz/mw-registrar": "^0.1.9",
"@jambonz/realtimedb-helpers": "^0.4.0",
"@jambonz/realtimedb-helpers": "^0.4.1",
"@jambonz/stats-collector": "^0.1.5",
"aws-sdk": "^2.848.0",
"bent": "^7.3.12",
@@ -36,12 +36,13 @@
"debug": "^4.3.1",
"deepcopy": "^2.1.0",
"drachtio-fsmrf": "^2.0.7",
"drachtio-srf": "^4.4.47",
"drachtio-srf": "^4.4.48",
"express": "^4.17.1",
"ip": "^1.1.5",
"moment": "^2.29.1",
"parse-url": "^5.0.2",
"pino": "^6.11.1",
"snakecase-keys": "^3.2.1",
"uuid": "^8.3.2",
"verify-aws-sns-signature": "^0.0.6",
"xml2js": "^0.4.23"