add custom nye headers to completed hook (#1552)

This commit is contained in:
Sam Machin
2026-05-21 15:02:28 +01:00
committed by GitHub
parent c1a7f1146a
commit d84690dfb1
6 changed files with 60 additions and 16 deletions
+6 -4
View File
@@ -54,22 +54,24 @@ class AdultingCallSession extends CallSession {
return this.callInfo.callSid;
}
_callerHungup() {
this._hangup('caller');
_callerHungup(req) {
this._hangup('caller', req);
}
_jambonzHangup() {
this._hangup();
}
_hangup(terminatedBy = 'jambonz') {
_hangup(terminatedBy = 'jambonz', req) {
if (this.dlg.connectTime) {
const duration = moment().diff(this.dlg.connectTime, 'seconds');
const headers = this._extractCustomHeaders(req);
this.rootSpan.setAttributes({'call.termination': `hangup by ${terminatedBy}`});
this.callInfo.callTerminationBy = terminatedBy;
this.emit('callStatusChange', {
callStatus: CallStatus.Completed,
duration
duration,
...(headers && {headers})
});
}
this.logger.info(`InboundCallSession: ${terminatedBy} hung up`);
+12
View File
@@ -124,6 +124,14 @@ class CallInfo {
return this._customerData;
}
set sipHeaders(obj) {
this._sipHeaders = obj;
}
get sipHeaders() {
return this._sipHeaders;
}
toJSON() {
const obj = {
callSid: this.callSid,
@@ -150,6 +158,10 @@ class CallInfo {
Object.assign(obj, {customerData: this._customerData});
}
if (this._sipHeaders) {
Object.assign(obj, {headers: this._sipHeaders});
}
if (JAMBONES_API_BASE_URL) {
Object.assign(obj, {apiBaseUrl: JAMBONES_API_BASE_URL});
}
+14 -1
View File
@@ -2664,6 +2664,18 @@ Duration=${duration} `
assert(false, 'subclass responsibility to override this method');
}
_extractCustomHeaders(req) {
const dominated = ['via', 'from', 'to', 'call-id', 'cseq', 'max-forwards',
'content-length', 'contact', 'route', 'record-route', 'content-type',
'authorization', 'proxy-authorization', 'www-authenticate', 'proxy-authenticate'];
if (!req || !req.headers) return null;
const headers = {};
Object.keys(req.headers).forEach((h) => {
if (!dominated.includes(h)) headers[h] = req.headers[h];
});
return Object.keys(headers).length ? headers : null;
}
/**
* called when the jambonz has hung up. Provided for subclasses to override
* in order to apply logic at this point if needed.
@@ -3053,7 +3065,7 @@ Duration=${duration} `
* @param {number} sipStatus - current sip status
* @param {number} [duration] - duration of a completed call, in seconds
*/
async _notifyCallStatusChange({callStatus, sipStatus, sipReason, duration}) {
async _notifyCallStatusChange({callStatus, sipStatus, sipReason, duration, headers}) {
if (this.callMoved) return;
// manage record all call.
@@ -3077,6 +3089,7 @@ Duration=${duration} `
this.callInfo.updateCallStatus(callStatus, sipStatus, sipReason);
if (typeof duration === 'number') this.callInfo.duration = duration;
if (headers) this.callInfo.sipHeaders = headers;
this.executeStatusCallback(callStatus, sipStatus);
// update calls db
+6 -4
View File
@@ -85,8 +85,8 @@ class InboundCallSession extends CallSession {
/**
* This is invoked when the caller hangs up, in order to calculate the call duration.
*/
_callerHungup() {
this._hangup('caller');
_callerHungup(req) {
this._hangup('caller', req);
}
_jambonzHangup(reason) {
@@ -99,7 +99,7 @@ class InboundCallSession extends CallSession {
this._callReleased();
}
_hangup(terminatedBy = 'jambonz') {
_hangup(terminatedBy = 'jambonz', req) {
if (this.dlg === null) {
this.logger.info('InboundCallSession:_hangup - race condition, dlg cleared by app hangup');
return;
@@ -107,11 +107,13 @@ class InboundCallSession extends CallSession {
this.logger.info(`InboundCallSession: ${terminatedBy} hung up`);
assert(this.dlg.connectTime);
const duration = moment().diff(this.dlg.connectTime, 'seconds');
const headers = this._extractCustomHeaders(req);
this.rootSpan.setAttributes({'call.termination': `hangup by ${terminatedBy}`});
this.callInfo.callTerminationBy = terminatedBy;
this.emit('callStatusChange', {
callStatus: CallStatus.Completed,
duration
duration,
...(headers && {headers})
});
this._callReleased();
this.req.removeAllListeners('cancel');
+5 -4
View File
@@ -51,21 +51,22 @@ class RestCallSession extends CallSession {
/**
* This is invoked when the called party hangs up, in order to calculate the call duration.
*/
_callerHungup() {
this._hangup('caller');
_callerHungup(req) {
this._hangup('caller', req);
}
_jambonzHangup() {
this._hangup();
}
_hangup(terminatedBy = 'jambonz') {
_hangup(terminatedBy = 'jambonz', req) {
if (this.restDialTask) {
this.restDialTask.turnOffAmd();
}
this.callInfo.callTerminationBy = terminatedBy;
const duration = moment().diff(this.dlg.connectTime, 'seconds');
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
const headers = this._extractCustomHeaders(req);
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration, ...(headers && {headers})});
this.logger.info(`RestCallSession: called party hung up by ${terminatedBy}`);
this._callReleased();
}
+17 -3
View File
@@ -265,10 +265,11 @@ class SingleDialer extends Emitter {
}
this.dlg
.on('destroy', () => {
.on('destroy', (req) => {
const duration = moment().diff(connectTime, 'seconds');
const headers = this._extractCustomHeaders(req);
this.logger.debug('SingleDialer:exec called party hung up');
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration, ...(headers && {headers})});
this.ep && this.ep.destroy();
})
.on('refresh', () => this.logger.info('SingleDialer:exec - dialog refreshed by uas'))
@@ -530,7 +531,19 @@ class SingleDialer extends Emitter {
}
}
_notifyCallStatusChange({callStatus, sipStatus, sipReason, duration}) {
_extractCustomHeaders(req) {
const dominated = ['via', 'from', 'to', 'call-id', 'cseq', 'max-forwards',
'content-length', 'contact', 'route', 'record-route', 'content-type',
'authorization', 'proxy-authorization', 'www-authenticate', 'proxy-authenticate'];
if (!req || !req.headers) return null;
const headers = {};
Object.keys(req.headers).forEach((h) => {
if (!dominated.includes(h)) headers[h] = req.headers[h];
});
return Object.keys(headers).length ? headers : null;
}
_notifyCallStatusChange({callStatus, sipStatus, sipReason, duration, headers}) {
assert((typeof duration === 'number' && callStatus === CallStatus.Completed) ||
(!duration && callStatus !== CallStatus.Completed),
'duration MUST be supplied when call completed AND ONLY when call completed');
@@ -538,6 +551,7 @@ class SingleDialer extends Emitter {
if (this.callInfo) {
this.callInfo.updateCallStatus(callStatus, sipStatus, sipReason);
if (typeof duration === 'number') this.callInfo.duration = duration;
if (headers) this.callInfo.sipHeaders = headers;
try {
this.notifier.request('call:status', this.application.call_status_hook, this.callInfo.toJSON());
} catch (err) {