SIGN UP

IVR module usage example: multi-level menu

IVR module usage example: multi-level menu

Most of IVRs nowadays provide caller with the number of options that can be chosen in a multi-level menu. While this kind of IVR can be built using vanilla VoxEngine javascript code, it's better to use helper module (Modules.IVR) we created to simplify the process. It lets developers build IVR menus in scenarios much faster and write less code for that. Let's build the following IVR using the module:

IVR scheme

It's rather simple, but have many input types: noinput, select, inputfixed and inputunknown. VoxEngine scenario for this IVR menu:

require(Modules.IVR); // enable IVR module

let inboundCall;
let outboundCall;
let introState;
let menuState;
let timeState;
let extState;
let fwdState;
let fwdNumber = null;

// Splice function for String class
String.prototype.splice = function (idx, rem, s) {
  return (this.slice(0, idx) + s + this.slice(idx + Math.abs(rem)));
};

function initIVR() {
  const now = new Date();
  /**
   * State that just says current UTC time
   */
  const timeState = new IVRState('time', {
    type: 'noinput',
    prompt: {
      say: `Current time in UTC time zone is ${now.getUTCHours()}:${now.getUTCMinutes()}`,
      lang: Language.US_ENGLISH_FEMALE
    }
  });

  /**
   * State that waits for 3-digit extension number
   * If 100 was entered - make a call to some employee
   */
  const extState = new IVRState('extension', {
    type: 'inputfixed',
    inputLength: 3,
    prompt: {
      say: 'Please enter the extension of the person you are trying to reach',
      lang: Language.US_ENGLISH_FEMALE
    }
  }, (data) => {
    // Extension has been entered
    if (data === '100') {
      inboundCall.say('Forwarding your call to John Connor, please wait', Language.US_ENGLISH_FEMALE);
      inboundCall.addEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished);
    } else {
      inboundCall.say(`There is no person with extension ${data}.`, Language.US_ENGLISH_FEMALE);
      inboundCall.addEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished2);
    }
  }, (data) => {
    // Timeout
    extState.enter(inboundCall);
  });

  /**
   * State that waits for US phone number and #
   * if valid US phone number was specified it forwards call to this number
   */
  const fwdState = new IVRState('callforward', {
    type: 'inputunknown',
    terminateOn: '#',
    prompt: {
      say: 'Please specify US phone number in international format and press pound',
      lang: Language.US_ENGLISH_FEMALE
    }
  }, (data) => {
    // Input finished
    const number = data.replace('#', '');
    // Check if the specified number is valid US phone number
    Net.httpRequest(`http://api.phone-validator.net/api/v2/verify?PhoneNumber=${number}&CountryCode=us`,
      (e) => {
        if (e.code === 200) {
          const result = JSON.parse(e.text);
          if (result.status === 'VALID_CONFIRMED') {
            fwdNumber = number;
            inboundCall.say('Forwarding your call to specified phone number, please wait', Language.US_ENGLISH_FEMALE);
            inboundCall.addEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished);
          } else {
            inboundCall.say('You have specified wrong phone number.', Language.US_ENGLISH_FEMALE);
            inboundCall.addEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished3);
          }
        } else {
          inboundCall.say('Something went wrong, please try again.', Language.US_ENGLISH_FEMALE);
          inboundCall.addEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished3);
        }
      });
  }, (data) => {
    // Timeout
    fwdState.enter(inboundCall);
  });

  /**
   * Menu state - 3 options
   */
  const menuState = new IVRState('menu', {
    type: 'select',
    prompt: {
      say: 'Press 1 to say time, press 2 enter extension number, press 3 to forward call to specified US number.',
      lang: Language.US_ENGLISH_FEMALE
    },
    nextStates: {
      '1': timeState,
      '2': extState,
      '3': fwdState
    },
  }, null, () => {
    // Timeout
    menuState.enter(inboundCall);
  });

  /**
   * Intro state
   */
  introState = new IVRState('intro', {
    type: 'noinput',
    prompt: {
      say: 'Hello, welcome to VoxImplant demo!',
      lang: Language.US_ENGLISH_FEMALE
    },
    nextState: menuState
  });

  timeState.settings.nextState = menuState;

}

// Forwards call to phone number
function handleFwdPlaybackFinished(e) {
  inboundCall.removeEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished);
  const callerId = '+1234567890'; // Rented or verified phone number
  if (fwdNumber == null) {
    // Calling John
    outboundCall = VoxEngine.callPSTN("John's number", callerId);
  } else {
    // Calling specified phone number
    outboundCall = VoxEngine.callPSTN(fwdNumber, callerId);
  }
  outboundCall.addEventListener(CallEvents.Connected, (e) => {
    VoxEngine.sendMediaBetween(e.call, inboundCall);
  });
  outboundCall.addEventListener(CallEvents.Failed, VoxEngine.terminate);
  outboundCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
}

// Wrong input - return to extState
function handleFwdPlaybackFinished2(e) {
  inboundCall.removeEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished2);
  extState.enter(inboundCall);
}

// Wrong input - return to fwdState
function handleFwdPlaybackFinished3(e) {
  inboundCall.removeEventListener(CallEvents.PlaybackFinished, handleFwdPlaybackFinished3);
  fwdState.enter(inboundCall);
}

// Init IVR states after the session start
VoxEngine.addEventListener(AppEvents.Started, (e) => {
  initIVR();
});

// Handle incoming call
VoxEngine.addEventListener(AppEvents.CallAlerting, (e) => {
  inboundCall = e.call;
  // add event listeners
  inboundCall.addEventListener(CallEvents.Connected, handleCallConnected);
  inboundCall.addEventListener(CallEvents.Disconnected, handleCallDisconnected);
  inboundCall.answer(); // answer the call
});

// After call connected enter introState
function handleCallConnected(e) {
  introState.enter(inboundCall);
}

function handleCallDisconnected(e) {
  // Just finish the session
  VoxEngine.terminate();
}

This works exactly as it appears: IVR suggests a caller choose one of the options:

  • press 1 to know the current time. Nothing else.
  • press 2 to enter an extension number of an employee. In the code above, it can be only the "100" extension, as specified on line 45-47. If the extension number "100" is specified, a scenario will try to make a PSTN call to the number specified on line 136. It should be a rented Voximplant number. 
  • press 3 to forward a call to a US phone number. After choosing this option, a caller should enter a valid US phone number and press pound. The specified number can be any of valid US phone numbers, not necessarily a Voximplant's one.

Setup

Go to the Applications section of the Voximplant Control panel, create an IVR application and an IVR-scenario inside of it. Then create an IVR-rule in the Routing tab of the application with the following settings:

Finally, switch to the Numbers tab of the IVR application, choose the Available attach a virtual/real phone number to this application and the rule:

Finally, switch to the Numbers tab with the Attached and Available sections. Switch to Available, select a rented virtual/real Voximplant number, and click Attach. In the window opened, specify IVR-rule, then click Attach.

Now all incoming calls will be forwarded to the IVR-scenario and processed by it.

Tags:ivrcall center
B6A24216-9891-45D1-9D1D-E7359CEB8282 Created with sketchtool.

Comments(0)

Add your comment

Please complete this field.

Recommended

Sign up for a free Voximplant developer account or talk to our experts
SIGN UP