mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-21 17:17:58 +00:00
initial changes for queue webhooks
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
const Emitter = require('events');
|
const Emitter = require('events');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const {CallDirection, TaskPreconditions, CallStatus, TaskName} = require('../utils/constants');
|
const {CallDirection, TaskPreconditions, CallStatus, TaskName, KillReason} = require('../utils/constants');
|
||||||
const moment = require('moment');
|
const moment = require('moment');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const sessionTracker = require('./session-tracker');
|
const sessionTracker = require('./session-tracker');
|
||||||
@@ -402,7 +402,7 @@ class CallSession extends Emitter {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* we started a new app on the child leg, but nothing given for parent so hang him up */
|
/* we started a new app on the child leg, but nothing given for parent so hang him up */
|
||||||
this.currentTask.kill();
|
this.currentTask.kill(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,7 +539,7 @@ class CallSession extends Emitter {
|
|||||||
this.logger.info({tasks: listTaskNames(tasks)},
|
this.logger.info({tasks: listTaskNames(tasks)},
|
||||||
`CallSession:replaceApplication reset with ${tasks.length} new tasks, stack depth is ${this.stackIdx}`);
|
`CallSession:replaceApplication reset with ${tasks.length} new tasks, stack depth is ${this.stackIdx}`);
|
||||||
if (this.currentTask) {
|
if (this.currentTask) {
|
||||||
this.currentTask.kill();
|
this.currentTask.kill(this, KillReason.Replaced);
|
||||||
this.currentTask = null;
|
this.currentTask = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -548,7 +548,7 @@ class CallSession extends Emitter {
|
|||||||
if (this.isConfirmCallSession) this.logger.debug('CallSession:kill (ConfirmSession)');
|
if (this.isConfirmCallSession) this.logger.debug('CallSession:kill (ConfirmSession)');
|
||||||
else this.logger.info('CallSession:kill');
|
else this.logger.info('CallSession:kill');
|
||||||
if (this.currentTask) {
|
if (this.currentTask) {
|
||||||
this.currentTask.kill();
|
this.currentTask.kill(this);
|
||||||
this.currentTask = null;
|
this.currentTask = null;
|
||||||
}
|
}
|
||||||
this.tasks = [];
|
this.tasks = [];
|
||||||
|
|||||||
@@ -356,7 +356,7 @@ class Conference extends Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.maxParticipants === 'number' && this.maxParticipants > 1) {
|
if (typeof this.maxParticipants === 'number' && this.maxParticipants > 1) {
|
||||||
this.endpoint.api('conference', `${this.confName} set max_members ${this.maxParticipants}`)
|
this.ep.api('conference', `${this.confName} set max_members ${this.maxParticipants}`)
|
||||||
.catch((err) => this.logger.error(err, `Error setting max participants to ${this.maxParticipants}`));
|
.catch((err) => this.logger.error(err, `Error setting max participants to ${this.maxParticipants}`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -448,16 +448,16 @@ class Conference extends Task {
|
|||||||
async _playHook(cs, dlg, hook, allowed = [TaskName.Play, TaskName.Say, TaskName.Pause]) {
|
async _playHook(cs, dlg, hook, allowed = [TaskName.Play, TaskName.Say, TaskName.Pause]) {
|
||||||
assert(!this._playSession);
|
assert(!this._playSession);
|
||||||
const json = await cs.application.requestor.request(hook, cs.callInfo);
|
const json = await cs.application.requestor.request(hook, cs.callInfo);
|
||||||
|
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
||||||
|
|
||||||
const allowedTasks = json.filter((task) => allowed.includes(task.verb));
|
const allowedTasks = tasks.filter((t) => allowed.includes(t.name));
|
||||||
if (json.length !== allowedTasks.length) {
|
if (tasks.length !== allowedTasks.length) {
|
||||||
this.logger.debug({json, allowedTasks}, 'unsupported task');
|
this.logger.debug({tasks, allowedTasks}, 'unsupported task');
|
||||||
throw new Error(`unsupported verb in dial conference wait/enterHook: only ${JSON.stringify(allowed)}`);
|
throw new Error(`unsupported verb in conference waitHook: only ${JSON.stringify(allowed)}`);
|
||||||
}
|
}
|
||||||
this.logger.debug(`Conference:_playHook: executing ${json.length} tasks`);
|
this.logger.debug(`Conference:_playHook: executing ${tasks.length} tasks`);
|
||||||
|
|
||||||
if (json.length > 0) {
|
if (tasks.length > 0) {
|
||||||
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
|
|
||||||
this._playSession = new ConfirmCallSession({
|
this._playSession = new ConfirmCallSession({
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
application: cs.application,
|
application: cs.application,
|
||||||
@@ -514,9 +514,6 @@ class Conference extends Task {
|
|||||||
const functionName = `_on${capitalize(camelize(action))}`;
|
const functionName = `_on${capitalize(camelize(action))}`;
|
||||||
(Conference.prototype[functionName] || unhandled).bind(this, this.logger, cs, evt)() ;
|
(Conference.prototype[functionName] || unhandled).bind(this, this.logger, cs, evt)() ;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.logger.debug(`Conference#__onConferenceEvent: got unhandled custom event: ${eventName}`) ;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// conference event handlers
|
// conference event handlers
|
||||||
|
|||||||
@@ -110,7 +110,8 @@ class TaskDequeue extends Task {
|
|||||||
event: 'dequeue',
|
event: 'dequeue',
|
||||||
dequeueSipAddress: cs.srf.locals.localSipAddress,
|
dequeueSipAddress: cs.srf.locals.localSipAddress,
|
||||||
epUuid: ep.uuid,
|
epUuid: ep.uuid,
|
||||||
notifyUrl: getUrl(cs)
|
notifyUrl: getUrl(cs),
|
||||||
|
dequeuer: cs.callInfo.toJSON()
|
||||||
});
|
});
|
||||||
this.logger.info(`TaskDequeue:_dequeueUrl successfully sent POST to ${url}`);
|
this.logger.info(`TaskDequeue:_dequeueUrl successfully sent POST to ${url}`);
|
||||||
bridgeTimer = setTimeout(() => reject(new Error('bridge timeout')), 20000);
|
bridgeTimer = setTimeout(() => reject(new Error('bridge timeout')), 20000);
|
||||||
|
|||||||
@@ -1,6 +1,13 @@
|
|||||||
const Task = require('./task');
|
const Task = require('./task');
|
||||||
const makeTask = require('./make_task');
|
const makeTask = require('./make_task');
|
||||||
const {CallStatus, CallDirection, TaskName, TaskPreconditions, MAX_SIMRINGS} = require('../utils/constants');
|
const {
|
||||||
|
CallStatus,
|
||||||
|
CallDirection,
|
||||||
|
TaskName,
|
||||||
|
TaskPreconditions,
|
||||||
|
MAX_SIMRINGS,
|
||||||
|
KillReason
|
||||||
|
} = require('../utils/constants');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const placeCall = require('../utils/place-outdial');
|
const placeCall = require('../utils/place-outdial');
|
||||||
const sessionTracker = require('../session/session-tracker');
|
const sessionTracker = require('../session/session-tracker');
|
||||||
@@ -138,8 +145,15 @@ class TaskDial extends Task {
|
|||||||
if (this.epOther) this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector);
|
if (this.epOther) this._installDtmfDetection(cs, this.epOther, this.parentDtmfCollector);
|
||||||
await this._attemptCalls(cs);
|
await this._attemptCalls(cs);
|
||||||
await this.awaitTaskDone();
|
await this.awaitTaskDone();
|
||||||
|
<<<<<<< HEAD
|
||||||
await this.performAction(this.results);
|
await this.performAction(this.results);
|
||||||
if (this.epOther) this._removeDtmfDetection(cs, this.epOther);
|
if (this.epOther) this._removeDtmfDetection(cs, this.epOther);
|
||||||
|
=======
|
||||||
|
|
||||||
|
this.logger.debug({callSid: this.cs.callSid}, 'Dial:exec task is done, sending actionHook if any');
|
||||||
|
await this.performAction(this.results, this.killReason !== KillReason.Replaced);
|
||||||
|
this._removeDtmfDetection(cs, this.epOther);
|
||||||
|
>>>>>>> 2b782bc... bugfix for enqueue and dial when these tasks are replaced by LCC
|
||||||
this._removeDtmfDetection(cs, this.ep);
|
this._removeDtmfDetection(cs, this.ep);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error({err}, 'TaskDial:exec terminating with error');
|
this.logger.error({err}, 'TaskDial:exec terminating with error');
|
||||||
@@ -147,9 +161,14 @@ class TaskDial extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async kill(cs) {
|
async kill(cs, reason) {
|
||||||
super.kill(cs);
|
super.kill(cs);
|
||||||
|
<<<<<<< HEAD
|
||||||
if (this.epOther) this._removeDtmfDetection(this.cs, this.epOther);
|
if (this.epOther) this._removeDtmfDetection(this.cs, this.epOther);
|
||||||
|
=======
|
||||||
|
this.killReason = reason || KillReason.Hangup;
|
||||||
|
this._removeDtmfDetection(this.cs, this.epOther);
|
||||||
|
>>>>>>> 2b782bc... bugfix for enqueue and dial when these tasks are replaced by LCC
|
||||||
this._removeDtmfDetection(this.cs, this.ep);
|
this._removeDtmfDetection(this.cs, this.ep);
|
||||||
this._killOutdials();
|
this._killOutdials();
|
||||||
if (this.sd) {
|
if (this.sd) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ const Emitter = require('events');
|
|||||||
const ConfirmCallSession = require('../session/confirm-call-session');
|
const ConfirmCallSession = require('../session/confirm-call-session');
|
||||||
const normalizeJambones = require('../utils/normalize-jambones');
|
const normalizeJambones = require('../utils/normalize-jambones');
|
||||||
const makeTask = require('./make_task');
|
const makeTask = require('./make_task');
|
||||||
const {TaskName, TaskPreconditions, QueueResults} = require('../utils/constants');
|
const {TaskName, TaskPreconditions, QueueResults, KillReason} = require('../utils/constants');
|
||||||
const bent = require('bent');
|
const bent = require('bent');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
@@ -61,9 +61,10 @@ class TaskEnqueue extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async kill(cs) {
|
async kill(cs, reason) {
|
||||||
super.kill(cs);
|
super.kill(cs);
|
||||||
this.logger.info(`TaskEnqueue:kill ${this.queueName}`);
|
this.killReason = reason || KillReason.Hangup;
|
||||||
|
this.logger.info(`TaskEnqueue:kill ${this.queueName} with reason ${this.killReason}`);
|
||||||
this.emitter.emit('kill');
|
this.emitter.emit('kill');
|
||||||
this.notifyTaskDone();
|
this.notifyTaskDone();
|
||||||
}
|
}
|
||||||
@@ -98,7 +99,7 @@ class TaskEnqueue extends Task {
|
|||||||
queueTime: getElapsedTime(this.waitStartTime),
|
queueTime: getElapsedTime(this.waitStartTime),
|
||||||
queueResult: this.state
|
queueResult: this.state
|
||||||
};
|
};
|
||||||
await super.performAction(params);
|
await super.performAction(params, this.killReason !== KillReason.Replaced);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,18 +120,20 @@ class TaskEnqueue extends Task {
|
|||||||
resolve(this._doBridge(cs, dlg, ep));
|
resolve(this._doBridge(cs, dlg, ep));
|
||||||
})
|
})
|
||||||
.once('kill', async() => {
|
.once('kill', async() => {
|
||||||
try {
|
|
||||||
const members = await this._removeFromQueue(cs);
|
|
||||||
|
|
||||||
/* invoke account-level webhook for queue event notifications */
|
/* invoke account-level webhook for queue event notifications */
|
||||||
cs.performQueueWebhook({
|
if (!this.dequeued) {
|
||||||
event: 'leave',
|
try {
|
||||||
queue: this.data.name,
|
const members = await this._removeFromQueue(cs);
|
||||||
length: members,
|
cs.performQueueWebhook({
|
||||||
leaveReason: 'abandoned',
|
event: 'leave',
|
||||||
leaveTime: Date.now()
|
queue: this.data.name,
|
||||||
});
|
length: members,
|
||||||
} catch (err) {}
|
leaveReason: 'abandoned',
|
||||||
|
leaveTime: Date.now()
|
||||||
|
});
|
||||||
|
} catch (err) {}
|
||||||
|
}
|
||||||
|
|
||||||
if (this._playSession) {
|
if (this._playSession) {
|
||||||
this.logger.debug('killing waitUrl');
|
this.logger.debug('killing waitUrl');
|
||||||
@@ -237,7 +240,7 @@ class TaskEnqueue extends Task {
|
|||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.on('kill', () => {
|
.on('kill', () => {
|
||||||
this.logger.info('TaskEnqueue:_bridgeLocal ending with hangup from enqeue party');
|
this.logger.info(`TaskEnqueue:_bridgeLocal ending with ${this.killReason}`);
|
||||||
ep.unbridge().catch((err) => {});
|
ep.unbridge().catch((err) => {});
|
||||||
|
|
||||||
// notify partner that we dropped
|
// notify partner that we dropped
|
||||||
@@ -263,12 +266,26 @@ class TaskEnqueue extends Task {
|
|||||||
* @param {string} opts.epUuid uuid of the endpoint we need to bridge to
|
* @param {string} opts.epUuid uuid of the endpoint we need to bridge to
|
||||||
* @param {string} opts.dequeueSipAddress ip:port of the feature server hosting the other call
|
* @param {string} opts.dequeueSipAddress ip:port of the feature server hosting the other call
|
||||||
*/
|
*/
|
||||||
notifyQueueEvent(cs, opts) {
|
async notifyQueueEvent(cs, opts) {
|
||||||
if (opts.event === 'dequeue') {
|
if (opts.event === 'dequeue') {
|
||||||
if (this.bridgeNow) return;
|
if (this.bridgeNow) return;
|
||||||
this.logger.info({opts}, `TaskEnqueue:notifyDequeueEvent: leaving ${this.queueName} because someone wants me`);
|
this.logger.info({opts}, `TaskEnqueue:notifyDequeueEvent: leaving ${this.queueName} because someone wants me`);
|
||||||
assert(opts.dequeueSipAddress && opts.epUuid && opts.notifyUrl);
|
assert(opts.dequeueSipAddress && opts.epUuid && opts.notifyUrl);
|
||||||
this.emitter.emit('dequeue', opts);
|
this.emitter.emit('dequeue', opts);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {lengthOfList} = cs.srf.locals.dbHelpers;
|
||||||
|
const members = await lengthOfList(this.queueName);
|
||||||
|
this.dequeued = true;
|
||||||
|
cs.performQueueWebhook({
|
||||||
|
event: 'leave',
|
||||||
|
queue: this.data.name,
|
||||||
|
length: Math.max(members - 1, 0),
|
||||||
|
leaveReason: 'dequeued',
|
||||||
|
leaveTime: Date.now(),
|
||||||
|
dequeuer: opts.dequeuer
|
||||||
|
});
|
||||||
|
} catch (err) {}
|
||||||
}
|
}
|
||||||
else if (opts.event === 'hangup') {
|
else if (opts.event === 'hangup') {
|
||||||
this.emitter.emit('hangup');
|
this.emitter.emit('hangup');
|
||||||
|
|||||||
@@ -92,6 +92,10 @@
|
|||||||
"Hangup": "hangup",
|
"Hangup": "hangup",
|
||||||
"Timeout": "timeout"
|
"Timeout": "timeout"
|
||||||
},
|
},
|
||||||
|
"KillReason": {
|
||||||
|
"Hangup": "hangup",
|
||||||
|
"Replaced": "replaced"
|
||||||
|
},
|
||||||
"MAX_SIMRINGS": 10,
|
"MAX_SIMRINGS": 10,
|
||||||
"BONG_TONE": "tone_stream://v=-7;%(100,0,941.0,1477.0);v=-7;>=2;+=.1;%(1400,0,350,440)"
|
"BONG_TONE": "tone_stream://v=-7;%(100,0,941.0,1477.0);v=-7;>=2;+=.1;%(1400,0,350,440)"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user