Studento919 Studento919 - 1 year ago 163
Javascript Question

How to use Asterisk ARI with socket.io & Node.js

Have been recently getting to grips with asterisk, Linux, node.js and most recently socket.io so that I can eventually make real time web applications for asterisk.

So as an educated guess ave been able to see kinda that Node.js is like the middle man between Asterisk and Socket. But I have no idea how to pull information from asterisk to a web page via socket.io.

I have been working on socket.io and ave been at a lose for a couple of days on how to interlink them so I can for example, log events happening in stasis or pull out current calls in a conference call, just anything at this point and due to ARI being relatively new as far as am aware, its been a struggle figuring it out.

I have linked 3 files below to give you an idea of what ave been doing, bridge-mixed.js is based off an example given in the asterisk ARI documentation.

I can run the file via node.js, dial the extension I specified in my extensions.conf file, when the first user enters the conference play music, once the more than 1 user enters then stop the music.

As for the other two files, its just a basic socket.io app ave been working through step by step via a YouTube guide to understand how it works.

I just need anything as simple as a brief example on how to mold them or make them work together, to start making real time web applications for asterisk.

Even if I somehow am able to pull stasis events out to a web page via socket.io & Node.js.

Hopefully you guys can shed some insight or guidance as am really lost with this at the moment.

bridge-mixed.js

/*jshint node:true*/
'use strict';

var ari = require('ari-client');
var util = require('util');
var chanArr =[];

ari.connect('http://localhost:0001', 'asterisk', 'asterisk', clientLoaded);

// handler for client being loaded
function clientLoaded (err, client) {
if (err) {
throw err;
}

// find or create a holding bridge
var bridge = null;
client.bridges.list(function(err, bridges) {
if (err) {
throw err;
}

bridge = bridges.filter(function(candidate) {
return candidate.bridge_type === 'mixing';
})[0];

if (bridge) {
console.log(util.format('Using bridge %s', bridge.id));
} else {
client.bridges.create({type: 'mixing'}, function(err, newBridge) {
if (err) {
throw err;
}

bridge = newBridge;
console.log(util.format('Created bridge %s', bridge.id));
});
}
});

// handler for StasisStart event
function stasisStart(event, channel) {
console.log(util.format(
'Channel %s just entered our application, adding it to bridge %s',
channel.name,
bridge.id));



channel.answer(function(err) {
if (err) {
throw err;
}

bridge.addChannel({channel: channel.id}, function(err) {
chanArr.push(channel)

if (err) {
throw err;
}


//If else statement to start music for first user entering channel, music will stop once more than 1 enters the channel.
if(chanArr.length <= 1){
bridge.startMoh(function(err) {
if (err) {
throw err;
}
});
}else{
bridge.stopMoh(function(err) {
if (err) {
throw err;
}
});
}

});
});
}

// handler for StasisEnd event
function stasisEnd(event, channel) {
chanArr = null;
console.log(util.format(
'Channel %s just left our application', channel.name));
}

client.on('StasisStart', stasisStart);
client.on('StasisEnd', stasisEnd);

client.start('bridge-hold');
}


Then below is a very basic socket.io functionality and html page:

app.js

var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
nicknames = [];

server.listen(0001);

app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});

io.sockets.on('connection', function (socket) {
socket.on('new user', function (data, callback) {
if (nicknames.indexOf(data) != -1) {
callback(false);
} else {
callback(true);
socket.nickname = data;
nicknames.push(socket.nickname);
updateNicknames();
}
});

function updateNicknames() {
io.sockets.emit('usernames', nicknames);
}

socket.on('send message', function (data) {
io.sockets.emit('new message', {
msg : data,
nick : socket.nickname
});
});

socket.on('disconnect', function (data) {
if (!socket.nickname)
return;
nicknames.splice(nicknames.indexOf(socket.nickname), 1);
updateNicknames();
});
});


index.html

<html>
<head>
<title> Chat with socket.io and node.js</title>
<style>
#chat{
height:500px;
}

#contentWrap{
display:none;
}

#chatWrap{
float:left;
border:1px #000 solid;
}

.error{
color:red;
}

.whisper{
color:gray;
font-style:italic;
}


</style>
</head>
<body>

<div id="nickWrap">
<p>Enter a Username</p>
<p id="nickError"></p>
<form id="setNick">
<input size="35" id="nickname"></input>
<input type="submit"></input>
</form>
</div>

<div id="contentWrap">
<div id="chatWrap">
<div id="chat"></div>
<form id="send-message">
<input size="35" id="message"></input>
<input type="submit"></input>
</form>
</div>
<div id="users"></div>
</div>

<script src="https://code.jquery.com/jquery-1.10.2.min.js"></script>
<script src="https://cdn.socket.io/socket.io-1.3.6.js"></script>
<script>
jQuery(function($){
var socket = io.connect();
var $nickForm = $('#setNick');
var $nickError = $('#nickError');
var $nickBox = $('#nickname');
var $users = $('#users');
var $messageForm = $('#send-message');
var $messageBox = $('#message');
var $chat = $('#chat');

$nickForm.submit(function(e){
e.preventDefault();
socket.emit('new user', $nickBox.val(), function(data){
if(data){
$('#nickWrap').hide();
$('#contentWrap').show();
} else{
$nickError.html('That username is already taken! Try Again.');
}
});
$nickBox.val('');
});

socket.on('usernames', function(data){
var html ='';
for(i=0; i < data.length; i++){
html += data[i] + '<br/>'
}
$users.html(html);
});

$messageForm.submit(function(e){
e.preventDefault();
socket.emit('send message', $messageBox.val(), function(data){
$chat.append('<span class="error"><b>' + data + "</span><br/>");
});
$messageBox.val('');
});

socket.on('new message', function(data){
$chat.append('<span class="msg"><b>' + data.nick + ': </b>' + data.msg + "</span><br/>");
});

socket.on('whisper', function(data){
$chat.append('<span class="whisper"><b>' + data.nick + ': </b>' + data.msg + "</span><br/>");
});

});
</script>
</body>
</html>

Answer Source

So after a bit of trail and error, it is possible to have them working together by effectively merging the bridge-mixed.js & app.js files. Once this is done I can then start accessing the information on the asterisk side of things via the ARI client and start passing it to a real time web application acting as an asterisk front end via socket.io.

The code am posting currently is just appending the current caller name to the web page, but its a basic example it should be a good stepping stone to see what you can do with this, as the information is there can easily start using JQuery to start doing all the good stuff....e.g Muting calls, bridging kicking users from a conference. These are the things am currently working on and will update in the future.

I hope this helps someone.

app.js(ARI Client and Socket.io server side)

ARI Functions and socket.io server side.

var ari = require('ari-client');
var util = require('util');
var chanArr = [];
var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);

//ARI client
ari.connect('http://localhost:8088', 'asterisk', 'asterisk', clientLoaded);

function clientLoaded(err, client) {
    if (err) {
        throw err;
    }
    // find or create a holding bridges
    var bridge = null;
    client.bridges.list(function (err, bridges) {
        if (err) {
            throw err;
        }

        bridge = bridges.filter(function (candidate) {
                return candidate.bridge_type === 'mixing';
            })[0];

        if (bridge) {
            console.log(util.format('Using bridge %s', bridge.id));
        } else {
            client.bridges.create({
                type : 'mixing'
            }, function (err, newBridge) {
                if (err) {
                    throw err;
                }

                bridge = newBridge;
                console.log(util.format('Created bridge %s', bridge.id));
            });
        }
    });

    // handler for StasisStart event
    function stasisStart(event, channel) {
        console.log(util.format(
                'Channel %s just entered our application, adding it to bridge %s',
                channel.name,
                bridge.id));

        channel.answer(function (err) {
            if (err) {
                throw err;
            }

            bridge.addChannel({
                channel : channel.id
            }, function (err) {
                var id = chanArr.push(channel.name)
                    console.log("User: " + channel.name);
                if (err) {
                    throw err;
                }

                //If else statement to start music for first user entering channel, music will stop once more than 1 enters the channel.
                if (chanArr.length <= 1) {
                    bridge.startMoh(function (err) {
                        if (err) {
                            throw err;
                        }
                    });
                } else {
                    bridge.stopMoh(function (err) {
                        if (err) {
                            throw err;
                        }
                    });
                }

            });
        });
    }

    // handler for StasisEnd event
    function stasisEnd(event, channel) {
        chanArr = null;
        console.log(util.format(
                'Channel %s just left our application', channel.name));
    }
    client.on('StasisStart', stasisStart);
    client.on('StasisEnd', stasisEnd);
    client.start('bridge-hold');
}

//Socket.io logic here
server.listen(3009, function () {
    console.log('listening on *:3009');
});

app.use(express.static(__dirname + '/public'));
app.get('/', function (req, res) {
    res.sendfile(__dirname + "/testPage.html");
});

io.sockets.on('connection', function () {
    updateSip();
});

function updateSip() {
    io.sockets.emit('sip', chanArr);
}

testPage.html

Web application front end.

<html>
    <head>
        <title> Chat with socket.io and node.js</title>
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
            <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
                <link href="https://gitcdn.github.io/bootstrap-toggle/2.2.0/css/bootstrap-toggle.min.css" rel="stylesheet">
                    <link href="/css/style.css" rel="stylesheet" type="text/css">   
                    </head>
                    <body>

                        <nav class="navbar navbar-inverse navbar-fixed-top">
                            <div class="navbar-header">
                                <div class="navbar-brand">Asterisk ARI Test Application</div>
                            </div>
                            <div id="navbar" class="navbar-collapse collapse">
                            </div>
                        </nav>

                        <div class="main-bridge">
                            <div class="container">
                                <div class="jumbotron content-A">
                                    <form class="test-ari">
                                        <p class="lead">Enter the number you want to call.</p>
                                        <div class="input-group input-group-lg">
                                            <input type="tel" class="form-control" placeholder="Phone Number" aria-describedby="sizing-addon1" required="" /> 
                                            <span class="input-group-btn">
                                                <button class="btn btn-default" type="submit">Call Back Now</button>
                                            </span>
                                        </div>
                                    </form>
                                </div>
                            </div>
                        </div>

                        <div class="secondary-bridge" id="sip">
                            <h3 class="conf-head">Conference call</h3>
                            <div class="panel panel-default ">
                                <div class="panel-heading " >
                                    <h3 class="panel-title"><div id="sip"></div></h3>
                                </div>
                                <div class="panel-body">
                                    <input type="checkbox" data-on="Voice" data-off="Muted" checked data-toggle="toggle" data-onstyle="success" data-offstyle="danger">
                                        <button class="btn btn-default kick" id="kick" data-toggle="modal" data-target="#myModal" type="submit">Kick</button>
                                    </div>
                                </div>
                            </div>

                            <!-- Modal -->
                            <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
                                <div class="modal-dialog" role="document">
                                    <div class="modal-content">
                                        <div class="modal-header">
                                            <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                                            <h4 class="modal-title" id="myModalLabel">Kick user</h4>
                                        </div>
                                        <div class="modal-body">
                                            Are you you want to kick this user?
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-default" data-dismiss="modal">No</button>
                                            <button type="button" class="btn btn-primary">Yes</button>
                                        </div>
                                    </div>
                                </div>
                            </div>

                            <footer class="footer">
                                <p>&copy; User 2015</p>
                            </footer>

                            <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> 
                            <script src="https://gitcdn.github.io/bootstrap-toggle/2.2.0/js/bootstrap-toggle.min.js"></script>
                            <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
                                <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
                                <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> 
                                <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> 
                                <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> 
                                <script src="https://cdn.socket.io/socket.io-1.3.6.js"></script>
                                <script src="/js/test.js"></script>

                            </body>
                        </html>

test.js

Socket.io client side and some other bits of JQuery.

jQuery(function ($) {
    var socket = io.connect();
    var $sip = $('#sip');
    socket.on('sip', function (data) {
        var sip = '';
        for (i = 0; i < data.length; i++) {
            sip += data[i] + '<br/>'
        }
        $sip.append('<h3 class="conf-head">Conference call</h3> \
                                        <div class="panel panel-default ">\
                                            <div class="panel-heading " >\
                                                <h3 class="panel-title">' + sip + '</h3>\
                                            </div>\
                                            <div class="panel-body">\
                                                <input type="checkbox" data-on="Voice" data-off="Muted" checked data-toggle="toggle" data-onstyle="success" data-offstyle="danger">\
                                                    <button class="btn btn-default kick" id="kick" data-toggle="modal" data-target="#myModal" type="submit">Kick</button>\
                                                </div>\
                                            </div>');

    });

    $('.kick').click(function () {
        $('#myInput').focus()
    });
});
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download