mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-19 04:17:44 +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');
|
const normalizeJambones = require('./utils/normalize-jambones');
|
||||||
|
|
||||||
module.exports = function(srf, logger) {
|
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) {
|
function initLocals(req, res, next) {
|
||||||
const callSid = req.has('X-Retain-Call-Sid') ? req.get('X-Retain-Call-Sid') : uuidv4();
|
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;
|
req.locals.application_sid = application_sid;
|
||||||
}
|
}
|
||||||
if (req.has('X-Authenticated-User')) req.locals.originatingUser = req.get('X-Authenticated-User');
|
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();
|
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 {
|
else {
|
||||||
const uri = parseUri(req.uri);
|
const uri = parseUri(req.uri);
|
||||||
const arr = /context-(.*)/.exec(uri.user);
|
const arr = /context-(.*)/.exec(uri.user);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ function compareTasks(t1, t2) {
|
|||||||
case 'phone':
|
case 'phone':
|
||||||
return t1.number === t1.number;
|
return t1.number === t1.number;
|
||||||
case 'user':
|
case 'user':
|
||||||
|
case 'teams':
|
||||||
return t2.name === t1.name;
|
return t2.name === t1.name;
|
||||||
case 'sip':
|
case 'sip':
|
||||||
return t2.sipUri === t1.sipUri;
|
return t2.sipUri === t1.sipUri;
|
||||||
@@ -243,7 +244,9 @@ class TaskDial extends Task {
|
|||||||
async _attemptCalls(cs) {
|
async _attemptCalls(cs) {
|
||||||
const {req, srf} = cs;
|
const {req, srf} = cs;
|
||||||
const {getSBC} = srf.locals;
|
const {getSBC} = srf.locals;
|
||||||
|
const {lookupTeamsByAccount} = srf.locals.dbHelpers;
|
||||||
const sbcAddress = getSBC();
|
const sbcAddress = getSBC();
|
||||||
|
const teamsInfo = {};
|
||||||
|
|
||||||
if (!sbcAddress) throw new Error('no SBC found for outbound call');
|
if (!sbcAddress) throw new Error('no SBC found for outbound call');
|
||||||
const opts = {
|
const opts = {
|
||||||
@@ -252,6 +255,12 @@ class TaskDial extends Task {
|
|||||||
callingNumber: this.callerId || req.callingNumber
|
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 ms = await cs.getMS();
|
||||||
const timerRing = setTimeout(() => {
|
const timerRing = setTimeout(() => {
|
||||||
this.logger.info(`Dial:_attemptCall: ring no answer timer ${this.timeout}s exceeded`);
|
this.logger.info(`Dial:_attemptCall: ring no answer timer ${this.timeout}s exceeded`);
|
||||||
@@ -262,6 +271,7 @@ class TaskDial extends Task {
|
|||||||
try {
|
try {
|
||||||
t.url = t.url || this.confirmUrl;
|
t.url = t.url || this.confirmUrl;
|
||||||
t.method = t.method || this.confirmMethod || 'POST';
|
t.method = t.method || this.confirmMethod || 'POST';
|
||||||
|
if (t.type === 'teams') t.teamsInfo = teamsInfo;
|
||||||
const sd = placeCall({
|
const sd = placeCall({
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
application: cs.application,
|
application: cs.application,
|
||||||
@@ -393,6 +403,7 @@ class TaskDial extends Task {
|
|||||||
_bridgeEarlyMedia(sd) {
|
_bridgeEarlyMedia(sd) {
|
||||||
if (this.epOther && !this.bridged) {
|
if (this.epOther && !this.bridged) {
|
||||||
this.epOther.api('uuid_break', this.epOther.uuid);
|
this.epOther.api('uuid_break', this.epOther.uuid);
|
||||||
|
this.logger.debug('Dial:_bridgeEarlyMedia: bridging early media');
|
||||||
this.epOther.bridge(sd.ep);
|
this.epOther.bridge(sd.ep);
|
||||||
this.bridged = true;
|
this.bridged = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["phone", "sip", "user"]
|
"enum": ["phone", "sip", "user", "teams"]
|
||||||
},
|
},
|
||||||
"url": "string",
|
"url": "string",
|
||||||
"method": {
|
"method": {
|
||||||
@@ -212,7 +212,8 @@
|
|||||||
"name": "string",
|
"name": "string",
|
||||||
"number": "string",
|
"number": "string",
|
||||||
"sipUri": "string",
|
"sipUri": "string",
|
||||||
"auth": "#auth"
|
"auth": "#auth",
|
||||||
|
"vmail": "boolean"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"type"
|
"type"
|
||||||
|
|||||||
@@ -101,8 +101,10 @@ function installSrfLocals(srf, logger) {
|
|||||||
const {
|
const {
|
||||||
lookupAppByPhoneNumber,
|
lookupAppByPhoneNumber,
|
||||||
lookupAppBySid,
|
lookupAppBySid,
|
||||||
lookupAppByRealm
|
lookupAppByRealm,
|
||||||
} = require('jambonz-db-helpers')({
|
lookupAppByTeamsTenant,
|
||||||
|
lookupTeamsByAccount
|
||||||
|
} = require('@jambonz/db-helpers')({
|
||||||
host: process.env.JAMBONES_MYSQL_HOST,
|
host: process.env.JAMBONES_MYSQL_HOST,
|
||||||
user: process.env.JAMBONES_MYSQL_USER,
|
user: process.env.JAMBONES_MYSQL_USER,
|
||||||
password: process.env.JAMBONES_MYSQL_PASSWORD,
|
password: process.env.JAMBONES_MYSQL_PASSWORD,
|
||||||
@@ -138,6 +140,8 @@ function installSrfLocals(srf, logger) {
|
|||||||
lookupAppByPhoneNumber,
|
lookupAppByPhoneNumber,
|
||||||
lookupAppBySid,
|
lookupAppBySid,
|
||||||
lookupAppByRealm,
|
lookupAppByRealm,
|
||||||
|
lookupAppByTeamsTenant,
|
||||||
|
lookupTeamsByAccount,
|
||||||
updateCallStatus,
|
updateCallStatus,
|
||||||
retrieveCall,
|
retrieveCall,
|
||||||
listCalls,
|
listCalls,
|
||||||
|
|||||||
@@ -58,13 +58,24 @@ class SingleDialer extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async exec(srf, ms, opts) {
|
async exec(srf, ms, opts) {
|
||||||
|
opts = opts || {};
|
||||||
let uri, to;
|
let uri, to;
|
||||||
try {
|
try {
|
||||||
switch (this.target.type) {
|
switch (this.target.type) {
|
||||||
case 'phone':
|
case 'phone':
|
||||||
|
case 'teams':
|
||||||
assert(this.target.number);
|
assert(this.target.number);
|
||||||
uri = `sip:${this.target.number}@${this.sbcAddress}`;
|
uri = `sip:${this.target.number}@${this.sbcAddress}`;
|
||||||
to = this.target.number;
|
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;
|
break;
|
||||||
case 'user':
|
case 'user':
|
||||||
assert(this.target.name);
|
assert(this.target.name);
|
||||||
@@ -156,12 +167,23 @@ class SingleDialer extends Emitter {
|
|||||||
this.logger.debug(`SingleDialer:exec call connected: ${this.callSid}`);
|
this.logger.debug(`SingleDialer:exec call connected: ${this.callSid}`);
|
||||||
const connectTime = this.dlg.connectTime = moment();
|
const connectTime = this.dlg.connectTime = moment();
|
||||||
|
|
||||||
this.dlg.on('destroy', () => {
|
this.dlg
|
||||||
const duration = moment().diff(connectTime, 'seconds');
|
.on('destroy', () => {
|
||||||
this.logger.debug('SingleDialer:exec called party hung up');
|
const duration = moment().diff(connectTime, 'seconds');
|
||||||
this.emit('callStatusChange', {callStatus: CallStatus.Completed, duration});
|
this.logger.debug('SingleDialer:exec called party hung up');
|
||||||
this.ep.destroy();
|
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);
|
if (this.confirmHook) this._executeApp(this.confirmHook);
|
||||||
else this.emit('accept');
|
else this.emit('accept');
|
||||||
|
|||||||
56
package-lock.json
generated
56
package-lock.json
generated
@@ -294,6 +294,23 @@
|
|||||||
"integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
|
"integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@jambonz/db-helpers": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://npm.pkg.github.com/download/@jambonz/db-helpers/0.3.5/c91102adbb4850844cc84db212b80884f5c9944de2200c247c606ad5e3037c90",
|
||||||
|
"integrity": "sha512-7wp91KmtChAO9RyFm/Ud8vWPlur+W6+8HocXo1ozeQmIqd9U3+Tlww0Jsgn+UjxeKPq09qHejTXaHGBRiwjn0g==",
|
||||||
|
"requires": {
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"mysql2": "^2.0.2",
|
||||||
|
"uuid": "^7.0.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"uuid": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@protobufjs/aspromise": {
|
"@protobufjs/aspromise": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
@@ -355,9 +372,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/fs-extra": {
|
"@types/fs-extra": {
|
||||||
"version": "8.1.0",
|
"version": "8.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz",
|
||||||
"integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==",
|
"integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -368,9 +385,9 @@
|
|||||||
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
|
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
|
||||||
},
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "13.13.4",
|
"version": "13.13.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.6.tgz",
|
||||||
"integrity": "sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA=="
|
"integrity": "sha512-zqRj8ugfROCjXCNbmPBe2mmQ0fJWP9lQaN519hwunOgpHgVykme4G6FW95++dyNFDvJUk4rtExkVkL0eciu5NA=="
|
||||||
},
|
},
|
||||||
"abort-controller": {
|
"abort-controller": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -519,9 +536,9 @@
|
|||||||
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
||||||
},
|
},
|
||||||
"aws-sdk": {
|
"aws-sdk": {
|
||||||
"version": "2.669.0",
|
"version": "2.679.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.669.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.679.0.tgz",
|
||||||
"integrity": "sha512-kuVcSRpDzvkgmeSmMX6Q32eTOb8UeihhUdavMrvUOP6fzSU19cNWS9HAIkYOi/jrEDK85cCZxXjxqE3JGZIGcw==",
|
"integrity": "sha512-bsbu3Mbwqz2DHpVSSgaSoFYsNrW+54vXN74iG++cCE7AAg9vOgCKzGul8yhR4jxxlOmGlfP9kCNrkCpjQ/Uqkg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"buffer": "4.9.1",
|
"buffer": "4.9.1",
|
||||||
"events": "1.1.1",
|
"events": "1.1.1",
|
||||||
@@ -2365,15 +2382,6 @@
|
|||||||
"istanbul-lib-report": "^3.0.0"
|
"istanbul-lib-report": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jambonz-db-helpers": {
|
|
||||||
"version": "0.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/jambonz-db-helpers/-/jambonz-db-helpers-0.3.2.tgz",
|
|
||||||
"integrity": "sha512-j7AEgts+Bj1CFPiM0estFmWmdDTZKWbkeIPY1QT3BR0cLClzjqo9fmdCzLoDtk/NWMy7IPNEQpVHzEejxFHq9g==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "^4.1.1",
|
|
||||||
"mysql2": "^2.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jambonz-mw-registrar": {
|
"jambonz-mw-registrar": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/jambonz-mw-registrar/-/jambonz-mw-registrar-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/jambonz-mw-registrar/-/jambonz-mw-registrar-0.1.3.tgz",
|
||||||
@@ -2384,9 +2392,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"jambonz-realtimedb-helpers": {
|
"jambonz-realtimedb-helpers": {
|
||||||
"version": "0.2.12",
|
"version": "0.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/jambonz-realtimedb-helpers/-/jambonz-realtimedb-helpers-0.2.12.tgz",
|
"resolved": "https://registry.npmjs.org/jambonz-realtimedb-helpers/-/jambonz-realtimedb-helpers-0.2.13.tgz",
|
||||||
"integrity": "sha512-KcuXPSwxc9cHXz9uzuI9HxnHcKRHbwwq7BSFNZT61lF7KZdZDBozenDpP0Tmk/P65SN1gLbtWP4KM2W4FTtv/Q==",
|
"integrity": "sha512-7BAqnauT1DPjgW4OnLIUgJMxmJzABlxSWWXfmrki4QYc0GG/a3iV9JyDd4jtiumWtvygUaUmbht1AdnqLWnrJw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@google-cloud/text-to-speech": "^2.2.0",
|
"@google-cloud/text-to-speech": "^2.2.0",
|
||||||
"aws-sdk": "^2.631.0",
|
"aws-sdk": "^2.631.0",
|
||||||
@@ -3314,9 +3322,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
},
|
},
|
||||||
"safer-buffer": {
|
"safer-buffer": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@
|
|||||||
"drachtio-srf": "^4.4.28",
|
"drachtio-srf": "^4.4.28",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"ip": "^1.1.5",
|
"ip": "^1.1.5",
|
||||||
"jambonz-db-helpers": "^0.3.2",
|
"@jambonz/db-helpers": "^0.3.7",
|
||||||
"jambonz-mw-registrar": "^0.1.3",
|
"jambonz-mw-registrar": "^0.1.3",
|
||||||
"jambonz-realtimedb-helpers": "^0.2.13",
|
"jambonz-realtimedb-helpers": "^0.2.13",
|
||||||
"jambonz-stats-collector": "^0.0.3",
|
"jambonz-stats-collector": "^0.0.3",
|
||||||
|
|||||||
Reference in New Issue
Block a user