Macro to automatically send DTMF after call setup

This macro is commonly used to send *9 (via DTMF) to enable Pexip’s dual screen (people + people) feature, or *4 to toggle Pexip’s presentation in mix feature on/off. It can also be used to send other DTMF sequences, just edit line 11 to change from *9 to whatever is required.

Editing line 10 is a required step to dial this macro in for your environment. You will want to specifically change the example.com to match the calling domain used in your environment.

If you are using this macro in combination with Pexip CVI or Google Meet interop, you can leave the other 2 regexes on line 10 (.*@pex.ms & [a-zA-Z-]{10,12}@google.pexip.me) in place which allows for this same DTMF to be sent when use Pexip’s SIP Guest join for both Microsoft Teams and Google Meet.

This macro operates in the background, there is no panel or button exposed to users of the room system. This macro is widely deployed and used on systems registered to Pexip Infinity, Pexip Cloud, Webex cloud, VCS/Expressway, and CUCM.

/*
sendDtmfAfterSelectCallSetup 
WHAT:   This macro is designed to send dtmf after calls that match a regex are successfully setup
WHY:    This can be used to enter PIN codes, or enable other features with DTMF like dual screen, presentation in mix, change layout, etc. 
HOW:    If the dialed SIP URI matches any of the regexs in the selectUriRegexes array, the room system will send the "dtmfSequence" automatically after the call is connected.  
LIMITS: The macro is not designed for calls that use 2 stage dialing (with an IVR).  
*/

import xapi from 'xapi';
const selectUriRegexes = [/\d{9,12}@example\.com/,/.*@pex\.ms/,/[a-zA-Z\-]{10,12}@google\.pexip\.me/]; // modify this array of regexes to match your environment, you can add more regexes or just use one regex
const dtmfSequence = '*9'; // this DTMF sequence to be sent automatically after connecting to any calls matching selectUriRegexes
const msPause = 4000; // millisecond pause, used for delay between when the call status = connected and when the dtmfSequence is sent.
const preferSilentDTMF = true; // true: dtmfSequence sent with "silent feedback, or false: send audible DTMF 

let callId;

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

async function sendDtmfAfterPause (sequence, pause, trySilent) {
  console.debug(`Sleeping for ${pause} ms...`);
  await sleep(pause);
  console.info(`Sending DTMF sequence of ${sequence} now...`);
  if (trySilent) {
    try {
          await xapi.command('Call DTMFSend', {DTMFString: sequence,Feedback: 'Silent'});
        } catch (error) {
          console.info('Command to send silent DTMF errored, most likely an older system or firmware that does not support silent DTMF. Trying command without Feedback parameter. Error: ' + error);
          try {
            await xapi.command('Call DTMFSend', {DTMFString: sequence});
          }  catch (error) {
              console.error('Command to send audible DTMF failed with error: ' + error);
          }
        }
  }
  if (!trySilent) { 
    try {
          await xapi.command('Call DTMFSend', {DTMFString: sequence});
        } catch (error) {
          console.error('Command to send audible DTMF failed with error: ' + error);
        }
  }
}

const getCalledUri = async () => {
  try {
        const value = await xapi.Status.Call[callId].CallbackNumber.get();
        console.debug('CallbackNumber: ' + JSON.stringify(value));
        const uri = await value.split(':')[1];
        return uri;
      } catch (error) {
        console.error('Failed to retrieve Callback Number from callid, error: ' + error); 
      }
}

xapi.event.on('CallSuccessful', (event) => {
  console.info('Call successful event details: ' + JSON.stringify(event));
  callId = event.CallId;
  getCalledUri().then(alias => {
                  for (const regex of selectUriRegexes) {
                    if(regex.test(alias)) {
                      console.info(`Call with ${alias} matches selectUriRegexes list entry, DTMF sequence will be sent`);
                      sendDtmfAfterPause(dtmfSequence, msPause, preferSilentDTMF);
                      break; //exit the for loop if a match is found after sending DTMF
                    } else {
                      console.info(`Call with ${alias} does NOT match regex ${regex} in list`);
                    }
                  }
    }).catch(error => console.info(error));
});