mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-08-19 09:40:23 +00:00
revert media path check, use alertType, catch failed alert
This commit is contained in:
+35
-64
@@ -458,18 +458,6 @@ class CallSession extends Emitter {
|
||||
return this.accountInfo?.account?.service_provider_sid;
|
||||
}
|
||||
|
||||
/**
|
||||
* true when we are no longer anchored in the media path (media released to the SBC).
|
||||
* Media release is a feature of the dial verb; when the current task is a dial we consult its
|
||||
* media path, otherwise media is anchored at the media server for the life of the app.
|
||||
*/
|
||||
get isMediaReleased() {
|
||||
if (this.currentTask?.name === TaskName.Dial) {
|
||||
return this.currentTask.mediaPath !== MediaPath.FullMedia;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if this session was transferred from another server
|
||||
*/
|
||||
@@ -1792,67 +1780,50 @@ class CallSession extends Emitter {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prefer media-path DTMF: have the media server generate RFC 2833 tones directly into the
|
||||
* media stream (via send_dtmf), which the SBC/rtpengine simply forwards. This is reliable
|
||||
* regardless of whether rtpengine is transcoding the call. Sending SIP INFO to the SBC
|
||||
* instead relies on rtpengine 'play DTMF' injection, which silently no-ops on calls that
|
||||
* are not being transcoded (rtpengine has no DSP to synthesize the tone into).
|
||||
* this.ep is the endpoint facing the caller (A) leg; this.currentTask.ep is the endpoint
|
||||
* facing the dialed (B) leg.
|
||||
*
|
||||
* We choose the path based on the media routing (this.isMediaReleased), not merely on whether
|
||||
* an endpoint object exists. While anchored in the media path we send via send_dtmf, and if
|
||||
* that fails we deliberately do NOT fall back to SIP INFO: the media path is the reliable one,
|
||||
* so a failure here is a real error (falling back risks a silently-dropped or duplicated
|
||||
* digit). We log and raise an alert instead. SIP INFO is used only once media has been
|
||||
* released to the SBC, when there is no endpoint in the media path at all.
|
||||
*/
|
||||
if (!this.isMediaReleased) {
|
||||
const ep = callSid === this.callSid ? this.ep : this.currentTask?.ep;
|
||||
const ep = callSid === this.callSid ? this.ep : this.currentTask?.ep;
|
||||
if (ep?.connected) {
|
||||
/* anchored in the media path: have the media server generate RFC 2833 tones directly into
|
||||
* the media stream (via send_dtmf), which the SBC/rtpengine simply forwards -- reliable
|
||||
* regardless of whether rtpengine is transcoding the call. On failure we do NOT fall back to
|
||||
* SIP INFO (that path relies on rtpengine 'play DTMF' injection, which silently no-ops on
|
||||
* non-transcoded calls); a failure with an anchored endpoint is a real error, so we log and
|
||||
* raise an alert.
|
||||
*/
|
||||
try {
|
||||
if (!ep?.connected) throw new Error('no connected media endpoint for the targeted leg');
|
||||
this.logger.debug(`CallSession:_lccDtmf - sending ${digit}@${duration} via send_dtmf on ${ep.uuid}`);
|
||||
await ep.execute('send_dtmf', `${digit}@${duration}`);
|
||||
return;
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'CallSession:_lccDtmf - error sending DTMF via send_dtmf');
|
||||
const {writeAlerts} = this.srf.locals;
|
||||
if (writeAlerts) {
|
||||
try {
|
||||
writeAlerts({
|
||||
alert_type: 'dtmf-send-failure',
|
||||
account_sid: this.accountSid,
|
||||
service_provider_sid: this.serviceProviderSid,
|
||||
message: `failed to send DTMF digit ${digit} to ${callSid === this.callSid ? 'caller' : 'called party'}`,
|
||||
target_sid: this.callSid
|
||||
});
|
||||
} catch (alertErr) {
|
||||
this.logger.error({err: alertErr}, 'CallSession:_lccDtmf - error writing dtmf-send-failure alert');
|
||||
}
|
||||
}
|
||||
return;
|
||||
const {writeAlerts, AlertType} = this.srf.locals;
|
||||
const leg = callSid === this.callSid ? 'caller' : 'called party';
|
||||
writeAlerts({
|
||||
alert_type: AlertType.ERROR_UPDATING_CALL,
|
||||
account_sid: this.accountSid,
|
||||
service_provider_sid: this.serviceProviderSid,
|
||||
message: `DTMF send failure: could not send digit ${digit} to ${leg}`,
|
||||
target_sid: this.callSid
|
||||
}).catch((alertErr) => this.logger.error({err: alertErr}, 'CallSession:_lccDtmf - error writing alert'));
|
||||
}
|
||||
}
|
||||
|
||||
/* no endpoint in the media path (e.g. media released to the SBC): signal the SBC to inject
|
||||
* DTMF via SIP INFO
|
||||
*/
|
||||
try {
|
||||
const dlg = callSid === this.callSid ? this.dlg : this.currentTask.dlg;
|
||||
const res = await dlg.request({
|
||||
method: 'INFO',
|
||||
headers: {
|
||||
'Content-Type': 'application/dtmf',
|
||||
'X-Reason': 'Dtmf'
|
||||
},
|
||||
body: `Signal=${digit}
|
||||
else {
|
||||
/* media released to the SBC: signal via SIP INFO (relies on rtpengine 'play DTMF' injection) */
|
||||
try {
|
||||
const dlg = callSid === this.callSid ? this.dlg : this.currentTask.dlg;
|
||||
const res = await dlg.request({
|
||||
method: 'INFO',
|
||||
headers: {
|
||||
'Content-Type': 'application/dtmf',
|
||||
'X-Reason': 'Dtmf'
|
||||
},
|
||||
body: `Signal=${digit}
|
||||
Duration=${duration} `
|
||||
});
|
||||
this.logger.debug({res}, `CallSession:_lccDtmf
|
||||
});
|
||||
this.logger.debug({res}, `CallSession:_lccDtmf
|
||||
got response to INFO DTMF digit=${digit} and duration=${duration}`);
|
||||
return res;
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'CallSession:_lccDtmf - error sending INFO RFC 2833 DTMF');
|
||||
return res;
|
||||
} catch (err) {
|
||||
this.logger.error({err}, 'CallSession:_lccDtmf - error sending INFO RFC 2833 DTMF');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,13 +176,6 @@ class TaskDial extends Task {
|
||||
return this.data.exitMediaPath;
|
||||
}
|
||||
|
||||
/* current media routing for this dial: MediaPath.FullMedia (anchored at the media server),
|
||||
* PartialMedia or NoMedia (media released to the SBC)
|
||||
*/
|
||||
get mediaPath() {
|
||||
return this._mediaPath;
|
||||
}
|
||||
|
||||
get summary() {
|
||||
if (this.target.length === 1) {
|
||||
const target = this.target[0];
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@
|
||||
"@jambonz/realtimedb-helpers": "^0.8.21",
|
||||
"@jambonz/speech-utils": "^0.2.30",
|
||||
"@jambonz/stats-collector": "^0.1.10",
|
||||
"@jambonz/time-series": "^0.2.17",
|
||||
"@jambonz/time-series": "^0.5.1",
|
||||
"@jambonz/verb-specifications": "^0.1.13",
|
||||
"@modelcontextprotocol/sdk": "^1.9.0",
|
||||
"@opentelemetry/api": "^1.8.0",
|
||||
|
||||
Reference in New Issue
Block a user