Skip to content

Commit

Permalink
Merge pull request #18 from Cognigy/feature/avayavoicechannel112
Browse files Browse the repository at this point in the history
avaya 1.1.2
  • Loading branch information
alexteusz authored Feb 22, 2021
2 parents 42cc070 + 3b16786 commit 136e4cd
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 28 deletions.
8 changes: 6 additions & 2 deletions endpoint/avayaVoiceChannel/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ While creating Cognigy flow, you can use Avaya Extension nodes such as play,prom
**sms** - sends the sms messages in the voice channel flow. It's not needed for sms channel.
**redirect** - redirects current Cognigy flow to another flow and won't return. It requires the endpoint url of the target flow.
**locale** - sets the locale language and the voice type on CPaaS so that the intended language and the voice will be spoken to the customers.
**hours** - get the business hours with the settgins of holiday and hours of operations

# Deploy #
A Cognigy flow should be created and deployed with the endpoint type of Avaya voice channel and the ``Enable Input Transformer``, ``Enable Output Transformer`` and ``Enable Execution Finished Transformer`` have to be ``enabled``.

A Cognigy flow should be created and deployed with the endpoint type of Avaya voice channel with the optional parameter ``CpaaS Token`` which authenticates CPaaS's signature, and the ``Enable Input Transformer``, ``Enable Output Transformer`` and ``Enable Execution Finished Transformer`` have to be ``enabled``.
```
CPaaS Token
90b196e676074f52xb4de0a2d13af4f3
```
Next copy the endpoint url and paste it into the ``Voice Configuration`` -> ``WebLink`` of the phone number in Avaya CPaaS. Now it's ready for you to call the number to experience the Cognigy's flow.
Binary file modified endpoint/avayaVoiceChannel/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
117 changes: 91 additions & 26 deletions endpoint/avayaVoiceChannel/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,58 @@ interface CPaaSBody {
UrlBase: string;
Digits: string;
SpeechResult: string;
SpeechResultError: string;
Body: string;
SmsSid: string;
SessionId: string;
}

const COGNIGY_BASE_URL = 'https://endpoint-trial.cognigy.ai/';
const DEBUG_MODE = false;
const MAX_CONF_PARTIES = 2;
const DEFAULT_NUM_DIGITS = 1;
const DEFAULT_LANGUAGE = 'en-US';
const DEFAULT_GATHER_TIMEOUT = 3;
const DEFAULT_GATHER_TIMEOUT = 10;
const DEFAULT_VOICE = 'woman';
const DEFAULT_CALLER_ID = '18004567890';
const DEFAULT_API_VERSION = 'v2';
const HTTPS = 'https://';
const REDIRECT_PARAMS = '?PlayStatus=completed&SpeechResult=&SpeechResultError=redirect&Confidence=0';
/**
* creates CPaaS signature
*/
function getSignature(authToken, url, params) {
const data = Object.keys(params)
.sort()
.reduce((acc, key) => acc + key + params[key], url);
return crypto
.createHmac('sha1', authToken)
.update(data)
.digest('base64');
}
/**
* validate the signature
*/
function validSignature(endpoint, request, url) {
const requestSignature = request['headers']['x-zang-signature'];
if (requestSignature &&
(requestSignature == getSignature(endpoint.settings.cpaasToken,url,request.body)) ||
(requestSignature == getSignature(endpoint.settings.cpaasToken,url+REDIRECT_PARAMS,request.body))) {
return true;
} else {
return false;
}
}
/**
* get current timestamp
*/
function getTimestamp() {
const timestamp = new Date();
return (timestamp.getFullYear().toString() +
(timestamp.getMonth()+1).toString() +
timestamp.getDate().toString() +
timestamp.getHours().toString() +
timestamp.getMinutes().toString());
}

createRestTransformer({

Expand All @@ -58,28 +99,45 @@ createRestTransformer({
* every Endpoint, and the example above needs to be adjusted
* accordingly.
*/
const { body } = request as CPaaSRequest;
const { headers, body } = request;
const { host } = headers;
if (DEBUG_MODE) {
console.log('body='.concat(JSON.stringify(body)));
}
const { From, To, CallSid, Digits, SpeechResult, SmsSid, Body } = body;
const { AccountSid, ApiVersion, Digits, CallSid, From, SpeechResult, SpeechResultError, To, UrlBase, Body, SessionId } = body;
if (CallSid && endpoint?.settings?.cpaasToken && !validSignature(endpoint,request,UrlBase)) {
response.status(401).send('Unauthorized');
console.log('Unauthorized')
return null;
} else if (DEBUG_MODE) {
console.log('valid signature or turned off');
}
if (ApiVersion != DEFAULT_API_VERSION) {
throw Error('wrong CPaaS API version '.concat(ApiVersion));
}
if (SpeechResultError == 'redirect') {
return null;
}
const userId = From;
const sessionId = CallSid ? CallSid : SmsSid;
const sessionId = CallSid ? CallSid : (SessionId ? SessionId : (userId+getTimestamp()));
let sessionStorage = await getSessionStorage(userId,sessionId);
if (!sessionStorage.urlbase) {
sessionStorage.urlbase = host;
}
sessionStorage.From = From;
sessionStorage.To = To;
sessionStorage.cpaas_channel = CallSid ? 'call' : 'sms';
sessionStorage.cpaasChannel = CallSid ? 'call' : 'sms';
sessionStorage.numberOfDigits = DEFAULT_NUM_DIGITS;
let data = {"call": false,"sms":false, "phone":From};
let data = {"accountSid":AccountSid, "apiVersion":ApiVersion, "call": false,"sms":false, "phone":From};
const menu = sessionStorage['menu'] ? sessionStorage['menu'] : {};
let text = '';
switch (sessionStorage.cpaas_channel) {
switch (sessionStorage.cpaasChannel) {
case 'call':
text = Digits ? (menu[Digits] ? menu[Digits] : Digits.replace(/\s+/g, '')) : SpeechResult;
data.call = true;
break;
case 'sms':
text = Body;
text = Body.match(/^\d$/) ? (menu[Body] ? menu[Body] : Body) : Body;
data.sms = true;
break;
default:
Expand Down Expand Up @@ -127,23 +185,27 @@ createRestTransformer({
activities.forEach( (activity) => {
switch (activity.name) {
case 'handover':
let callerId = 'callerId="' + ((activity.activityParams.from != null && activity.activityParams.from != '') ? activity.activityParams.from : '{{To}}') + '"';
const handoverType = activity.activityParams.handoverType;
if (handoverType === "phone") {
const dest = activity.activityParams.destination;
if (dest == userId && callerId == '') {
callerId = 'callerId="' + DEFAULT_CALLER_ID + '"';
}
const cbUrl = (activity.activityParams.callbackUrl != "") ?
(' callbackUrl="' + activity.activityParams.callbackUrl + '"') : '';
let dial = '<Dial' + cbUrl + '>' + dest + '</Dial>';
let dial = '<Dial ' + callerId + cbUrl + '>' + dest + '</Dial>';
output.text = (output.text != null) ? (output.text + dial) : dial;
sessionStorage.dial = true;
} else if (handoverType === "sip") {
const cbUrl = (activity.activityParams.callbackUrl != "") ?
(' callbackUrl="' + activity.activityParams.callbackUrl + '"') : '';
const user = activity.activityParams.user;
const domain = activity.activityParams.domain;
const userName = activity.activityParams.connection.userName;
const username = activity.activityParams.connection.username;
const password = activity.activityParams.connection.password;
let sipUrl = user.concat("@".concat(domain));
let dial ='<Dial' + cbUrl + '><Sip username="' + userName + '" password="' + password + '">' + sipUrl + ';transport=tcp</Sip></Dial>';
let dial ='<Dial ' + callerId + cbUrl + '><Sip username="' + username + '" password="' + password + '">' + sipUrl + ';transport=tcp</Sip></Dial>';
output.text = (output.text != null) ? (output.text + dial) : dial;
sessionStorage.dial = true;
}
Expand All @@ -155,20 +217,20 @@ createRestTransformer({
sessionStorage['menu'] = menu;
sessionStorage.numberOfDigits = DEFAULT_NUM_DIGITS;
if (activity.activityParams.menuText) {
output.text = (sessionStorage.cpaas_channel == 'sms') ? activity.activityParams.menuText : (say + activity.activityParams.menuText + '</Say>');
output.text = (sessionStorage.cpaasChannel == 'sms') ? activity.activityParams.menuText : (say + activity.activityParams.menuText + '</Say>');
}
} else if (promptType === 'number') {
sessionStorage.numberOfDigits = activity.activityParams.numberOfDigits;
if (activity.activityParams.numberText) {
output.text = (sessionStorage.cpaas_channel == 'sms') ? activity.activityParams.numberText : (say + activity.activityParams.numberText + '</Say>');
output.text = (sessionStorage.cpaasChannel == 'sms') ? activity.activityParams.numberText : (say + activity.activityParams.numberText + '</Say>');
}
}
break;
case 'hangup':
sessionStorage.hangup = true;
break;
case 'play':
if (sessionStorage.cpaas_channel == 'call') {
if (sessionStorage.cpaasChannel == 'call') {
output.text += activity.activityParams.url ? ('<Play>' + activity.activityParams.url + '</Play>') : '';
} else {
output.text = activity.activityParams.text;
Expand All @@ -190,16 +252,17 @@ createRestTransformer({
break;
case 'sms':
sessionStorage.sms = true;
const to = activity.activityParams.to ? activity.activityParams.to : '';
if (sessionStorage.cpaas_channel == 'call') {
output.data.sms = '<Sms from="{{To}}" to="' + to + '">' + activity.activityParams.text + '</Sms>';
const from = ' from="' + ((activity.activityParams.from != null && activity.activityParams.from != '') ? activity.activityParams.from : '{{To}}') + '"';
const to = ' to="' + (activity.activityParams.to ? activity.activityParams.to : '') + '"';
if (sessionStorage.cpaasChannel == 'call') {
output.data.sms = '<Sms ' + from + to + '>' + activity.activityParams.text + '</Sms>';
} else {
output.text = activity.activityParams.text;
}
break;
case 'redirect':
sessionStorage.redirect = true;
output.text += activity.activityParams.url ? ('<Redirect>'+ activity.activityParams.url + '</Redirect>') : '';
output.text += activity.activityParams.url ? ('<Redirect method="POST">'+ activity.activityParams.url + '</Redirect>') : '';
break;
case 'locale':
sessionStorage.language = activity.activityParams.language;
Expand All @@ -209,7 +272,7 @@ createRestTransformer({
break;
}
});
} else if (output.text != null && (sessionStorage.cpaas_channel == 'call')) {
} else if (output.text != null && (sessionStorage.cpaasChannel == 'call')) {
            output.text = say + output.text + '</Say>';
        }
return output;
Expand All @@ -236,15 +299,15 @@ createRestTransformer({
* correct format according to the documentation of the specific Endpoint channel.
*/
handleExecutionFinished: async ({ processedOutput, outputs, userId, sessionId, endpoint, response }) => {
let url = COGNIGY_BASE_URL + endpoint.URLToken;
const sessionStorage = await getSessionStorage(userId, sessionId);
const url = HTTPS + sessionStorage.urlbase + '/' + endpoint.URLToken;
let cpaasResponse = 'default';
switch (sessionStorage.cpaas_channel) {
switch (sessionStorage.cpaasChannel) {
case 'call':
cpaasResponse = getCPaaSCallCmd(sessionStorage, url, outputs);
break;
case 'sms':
cpaasResponse = getCPaaSSmsCmd(sessionStorage, url, outputs);
cpaasResponse = getCPaaSSmsCmd(sessionStorage, url, sessionId, outputs);
break;
default:
break;
Expand Down Expand Up @@ -277,16 +340,18 @@ const getCPaaSCallCmd = (sessionStorage, url, outputs) => {
let smsCmds = sms ? outputs.map((t) => {return t.data.sms}).join('\n') : '';
let prompt = outputs.map((t) => {return t.text}).join('\n');
let numDigits = 'numDigits="' + numberOfDigits + '"';
let gather = '<Gather method="POST" action="'+url+'" input="speech dtmf" language="' + language + '" timeout="' + DEFAULT_GATHER_TIMEOUT + '" ' + numDigits + '>'+prompt+'</Gather>';
let gather = '<Gather method="POST" action="'+url+'" input="speech dtmf" language="' + language + '" timeout="' + DEFAULT_GATHER_TIMEOUT + '" ' + numDigits + '>'+prompt+'</Gather>'
+
'<Redirect method="POST">' + url + REDIRECT_PARAMS + '</Redirect>';
let cpaasResponse = '<Response>' + (record) + (smsCmds) + (ctrlcmd ? prompt : gather) + '</Response>';
return (cpaasResponse);
};

const getCPaaSSmsCmd = (sessionStorage, url, outputs) => {
const getCPaaSSmsCmd = (sessionStorage, url, sessionId, outputs) => {
let text = outputs.map((t) => {return t.text}).join('\n');
let From = sessionStorage.To;
let To = sessionStorage.From;
let FromTo = ' From="'+From+'" To="'+To+'"';
let cpaasResponse = '<Response><Sms action="'+url+FromTo+'>'+text+'</Sms></Response>';
let cpaasResponse = '<Response><Sms action="'+url+'?SessionId='+sessionId+'"'+FromTo+'>'+text+'</Sms></Response>';
return (cpaasResponse);
}

0 comments on commit 136e4cd

Please sign in to comment.