mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
add support for ms teams
This commit is contained in:
@@ -7,7 +7,7 @@ const parseUri = require('drachtio-srf').parseUri;
|
||||
const normalizeJambones = require('./utils/normalize-jambones');
|
||||
|
||||
module.exports = function(srf, logger) {
|
||||
const {lookupAppByPhoneNumber, lookupAppBySid, lookupAppByRealm} = srf.locals.dbHelpers;
|
||||
const {lookupAppByPhoneNumber, lookupAppBySid, lookupAppByRealm, lookupAppByTeamsTenant} = srf.locals.dbHelpers;
|
||||
|
||||
function initLocals(req, res, next) {
|
||||
const callSid = req.has('X-Retain-Call-Sid') ? req.get('X-Retain-Call-Sid') : uuidv4();
|
||||
@@ -21,6 +21,7 @@ module.exports = function(srf, logger) {
|
||||
req.locals.application_sid = application_sid;
|
||||
}
|
||||
if (req.has('X-Authenticated-User')) req.locals.originatingUser = req.get('X-Authenticated-User');
|
||||
if (req.has('X-MS-Teams-Tenant-FQDN')) req.locals.msTeamsTenant = req.get('X-MS-Teams-Tenant-FQDN');
|
||||
|
||||
next();
|
||||
}
|
||||
@@ -64,6 +65,10 @@ module.exports = function(srf, logger) {
|
||||
|
||||
}
|
||||
}
|
||||
else if (req.locals.msTeamsTenant) {
|
||||
app = await lookupAppByTeamsTenant(req.locals.msTeamsTenant);
|
||||
if (app) logger.debug({app}, `retrieved app for ms teams tenant ${req.locals.msTeamsTenant}`);
|
||||
}
|
||||
else {
|
||||
const uri = parseUri(req.uri);
|
||||
const arr = /context-(.*)/.exec(uri.user);
|
||||
|
||||
@@ -39,6 +39,7 @@ function compareTasks(t1, t2) {
|
||||
case 'phone':
|
||||
return t1.number === t1.number;
|
||||
case 'user':
|
||||
case 'teams':
|
||||
return t2.name === t1.name;
|
||||
case 'sip':
|
||||
return t2.sipUri === t1.sipUri;
|
||||
@@ -243,7 +244,9 @@ class TaskDial extends Task {
|
||||
async _attemptCalls(cs) {
|
||||
const {req, srf} = cs;
|
||||
const {getSBC} = srf.locals;
|
||||
const {lookupTeamsByAccount} = srf.locals.dbHelpers;
|
||||
const sbcAddress = getSBC();
|
||||
const teamsInfo = {};
|
||||
|
||||
if (!sbcAddress) throw new Error('no SBC found for outbound call');
|
||||
const opts = {
|
||||
@@ -252,6 +255,12 @@ class TaskDial extends Task {
|
||||
callingNumber: this.callerId || req.callingNumber
|
||||
};
|
||||
|
||||
if (this.target.find((t) => t.type === 'teams')) {
|
||||
const obj = await lookupTeamsByAccount(cs.accountSid);
|
||||
if (!obj) throw new Error('dial to ms teams not allowed; account must first be configured with teams info');
|
||||
Object.assign(teamsInfo, {tenant_fqdn: obj.tenant_fqdn, ms_teams_fqdn: obj.ms_teams_fqdn});
|
||||
}
|
||||
|
||||
const ms = await cs.getMS();
|
||||
const timerRing = setTimeout(() => {
|
||||
this.logger.info(`Dial:_attemptCall: ring no answer timer ${this.timeout}s exceeded`);
|
||||
@@ -262,6 +271,7 @@ class TaskDial extends Task {
|
||||
try {
|
||||
t.url = t.url || this.confirmUrl;
|
||||
t.method = t.method || this.confirmMethod || 'POST';
|
||||
if (t.type === 'teams') t.teamsInfo = teamsInfo;
|
||||
const sd = placeCall({
|
||||
logger: this.logger,
|
||||
application: cs.application,
|
||||
@@ -393,6 +403,7 @@ class TaskDial extends Task {
|
||||
_bridgeEarlyMedia(sd) {
|
||||
if (this.epOther && !this.bridged) {
|
||||
this.epOther.api('uuid_break', this.epOther.uuid);
|
||||
this.logger.debug('Dial:_bridgeEarlyMedia: bridging early media');
|
||||
this.epOther.bridge(sd.ep);
|
||||
this.bridged = true;
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["phone", "sip", "user"]
|
||||
"enum": ["phone", "sip", "user", "teams"]
|
||||
},
|
||||
"url": "string",
|
||||
"method": {
|
||||
@@ -212,7 +212,8 @@
|
||||
"name": "string",
|
||||
"number": "string",
|
||||
"sipUri": "string",
|
||||
"auth": "#auth"
|
||||
"auth": "#auth",
|
||||
"vmail": "boolean"
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
|
||||
@@ -101,8 +101,10 @@ function installSrfLocals(srf, logger) {
|
||||
const {
|
||||
lookupAppByPhoneNumber,
|
||||
lookupAppBySid,
|
||||
lookupAppByRealm
|
||||
} = require('jambonz-db-helpers')({
|
||||
lookupAppByRealm,
|
||||
lookupAppByTeamsTenant,
|
||||
lookupTeamsByAccount
|
||||
} = require('@jambonz/db-helpers')({
|
||||
host: process.env.JAMBONES_MYSQL_HOST,
|
||||
user: process.env.JAMBONES_MYSQL_USER,
|
||||
password: process.env.JAMBONES_MYSQL_PASSWORD,
|
||||
@@ -138,6 +140,8 @@ function installSrfLocals(srf, logger) {
|
||||
lookupAppByPhoneNumber,
|
||||
lookupAppBySid,
|
||||
lookupAppByRealm,
|
||||
lookupAppByTeamsTenant,
|
||||
lookupTeamsByAccount,
|
||||
updateCallStatus,
|
||||
retrieveCall,
|
||||
listCalls,
|
||||
|
||||
@@ -58,13 +58,24 @@ class SingleDialer extends Emitter {
|
||||
}
|
||||
|
||||
async exec(srf, ms, opts) {
|
||||
opts = opts || {};
|
||||
let uri, to;
|
||||
try {
|
||||
switch (this.target.type) {
|
||||
case 'phone':
|
||||
case 'teams':
|
||||
assert(this.target.number);
|
||||
uri = `sip:${this.target.number}@${this.sbcAddress}`;
|
||||
to = this.target.number;
|
||||
if ('teams' === this.target.type) {
|
||||
assert(this.target.teamsInfo);
|
||||
opts.headers = opts.headers || {};
|
||||
Object.assign(opts.headers, {
|
||||
'X-MS-Teams-FQDN': this.target.teamsInfo.ms_teams_fqdn,
|
||||
'X-MS-Teams-Tenant-FQDN': this.target.teamsInfo.tenant_fqdn
|
||||
});
|
||||
if (this.target.vmail === true) uri = `${uri};opaque=app:voicemail`;
|
||||
}
|
||||
break;
|
||||
case 'user':
|
||||
assert(this.target.name);
|
||||
@@ -156,12 +167,23 @@ class SingleDialer extends Emitter {
|
||||
this.logger.debug(`SingleDialer:exec call connected: ${this.callSid}`);
|
||||
const connectTime = this.dlg.connectTime = moment();
|
||||
|
||||
this.dlg.on('destroy', () => {
|
||||
const duration = moment().diff(connectTime, 'seconds');
|
||||
this.logger.debug('SingleDialer:exec called party hung up');
|
||||
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
|
||||
this.ep.destroy();
|
||||
});
|
||||
this.dlg
|
||||
.on('destroy', () => {
|
||||
const duration = moment().diff(connectTime, 'seconds');
|
||||
this.logger.debug('SingleDialer:exec called party hung up');
|
||||
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
|
||||
this.ep.destroy();
|
||||
})
|
||||
.on('refresh', () => this.logger.info('SingleDialer:exec - dialog refreshed by uas'))
|
||||
.on('modify', async(req, res) => {
|
||||
try {
|
||||
const newSdp = await this.ep.modify(req.body);
|
||||
res.send(200, {body: newSdp});
|
||||
this.logger.info({offer: req.body, answer: newSdp}, 'SingleDialer:exec: handling reINVITE');
|
||||
} catch (err) {
|
||||
this.logger.error(err, 'Error handling reinvite');
|
||||
}
|
||||
});
|
||||
|
||||
if (this.confirmHook) this._executeApp(this.confirmHook);
|
||||
else this.emit('accept');
|
||||
|
||||
Reference in New Issue
Block a user