Working Voximplant example code for Jigasi SIP dial-in

It took us a while to set up dial in using Voximplant on our private instance. To help others, here’s working code for a Voximplant scenario. It uses a list of fixed rooms, which may not be what everyone needs, but I still think it’s helpful to get started and implement other scenarios. The Voximplant docs are pretty good to implement advanced features. We used the docker image to install jitsi, so the muc stuff is specific to the docker image.

// cf. https://voximplant.com/blog/ivr-module-usage-example
// cf. https://voximplant.com/docs/references/voxengine/ivr

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

// room id (menu selection) : room name
// currently hardcode to 2-digit extensions, see below
const rooms = [
    { extension: '10',  name : 'abc', spoken : 'a b c' },
    { extension: '11',  name : 'test'},
];

let callAlerting;
let selectedRoom;

function generateMenuPrompt(rooms) {
    let msg = 'Dial ';
    for (const [i, room] of rooms.entries()) {
        if (i === rooms.length - 1) {
            msg += ' or ';
        }
        msg += room.extension + ' for room ' + getSpoken(room);
        if (i !== rooms.length - 1) {
            msg +=  ', ... ';
        }
    }
    msg += '.';
    return msg;
}

function getDisplayName(callerid) {
    // TODO can add some logic here
    return callerid;
}

function getSpoken(room) {
    return ' ' + (room.spoken || room.name) + ' ';
}

function joinRoom(room) {
    const inboundCall = callAlerting.call;

    const outboundCall = VoxEngine.callUser({
        username     : 'meet',
        callerid     : callAlerting.callerid,
        displayName  : getDisplayName(callAlerting.callerid),
        extraHeaders : { 'X-Room-Name'    : encodeURI(room.name) + '@muc.meet.jitsi',
                         'VI-CallTimeout' : 5,
                       },
        video        : false,
        scheme       : callAlerting.scheme,
    });
    
    VoxEngine.easyProcess(inboundCall, outboundCall);

    // cf. https://github.com/voximplant/easyprocess/blob/master/easyprocess.js
    outboundCall.removeEventListener(CallEvents.Failed);
    outboundCall.addEventListener(CallEvents.Failed, () => {
        msg = 'The conference in the room ' + getSpoken(room) + ' has not been started.';
        msg += ' Please try again after the conference has been started.';
        inboundCall.say(msg);
        inboundCall.addEventListener(CallEvents.PlaybackFinished, VoxEngine.terminate);
    });
}

function handleJoinRoom(e) {
    inboundCall.removeEventListener(CallEvents.PlaybackFinished, handleJoinRoom);
    joinRoom(selectedRoom);
}

function handleCallConnected(e) {
    const introPrompt = 'Welcome to our Jitsi server.';
    const ivr = new IVRState('menu', {
        type        : 'inputfixed',
        inputLength : 2,
        prompt      : { say  : introPrompt + ' ' + generateMenuPrompt(rooms) },
        timeout: 10000 // ms
    }, (selectedExtension) => {
        selectedRoom = rooms.find( ({ extension }) => extension === selectedExtension );
        if (!selectedRoom) {
            inboundCall.say('Invalid room.');
            inboundCall.addEventListener(CallEvents.PlaybackFinished, VoxEngine.terminate);
        } else {
            inboundCall.say(
                'You're joining the room ' + getSpoken(selectedRoom) + ' .');
            inboundCall.addEventListener(CallEvents.PlaybackFinished, handleJoinRoom);
        }
    }, () => {
        // timeout
        VoxEngine.terminate();
    });
    
    ivr.enter(e.call);
}

// handle incoming call
VoxEngine.addEventListener(AppEvents.CallAlerting, (e) => {
    callAlerting = e;

    // add event listeners
    inboundCall = e.call;
    inboundCall.startEarlyMedia();
    inboundCall.addEventListener(CallEvents.Connected, handleCallConnected);
    inboundCall.addEventListener(CallEvents.Disconnected, VoxEngine.terminate);
    inboundCall.answer()
});

Tim! My man, I just wanted to thank you… been trying to get the voximplant recent tutorial working and it wasn’t working at all with the docker Jitsi code (something about the formatting of the requests and the internal domains)

Yours works wonderfully but there is a slight bug here

‘You’re joining the room ’ + getSpoken(selectedRoom) + ’ .’);

You terminated the string with the apostrophe

Ha, great to hear it’s helpful.

The bug: Hehe, yes, one should never post untested code… I had translated the strings from German to English before posting the code here, and that broke it of course. I can’t edit the post anymore but the fix is fortunately obvious. :slight_smile:

Hi Tim! Thought I’d let you know that I’ve added dynamic dialing to the code - figured since the community gave me the initial code I’d give it back in case it’s useful to you or anyone else. Again for the Docker Jitsi Meet and this uses the Jitsi conference mapper :slight_smile:

Never done much in JS before so it can probably be improved

Ah, if you want to make this a repo for working example, feel free to add my code as a fixed-room variant. (Under whatever license you want, I’m licensing it under CC0).

Thanks Tim! I’ll add it :slight_smile: