Gửi thông tin cuộc gọi realtime từ máy chủ Asterisk đến client CRM

Một giải pháp liên kết với tổng đài Asterisk để thông báo cho CRM mỗi khi có cuộc gọi đến/đi realtime, sử dụng Socket.io.

Cài đặt các gói thư viện

npm install --save express \
    socket.io \
    asterisk-manager

Cài đặt pm2 – chạy app Node.js lúc khởi động

npm install -g pm2

Mã nguồn chương trình

Chương trình sau xử lý chuỗi để lấy thông tin cuộc gọi đến và đi (số extension phía chủ và số điện thoại phía khách) nhờ giao diện AMI (Asterisk Manager Interface), sau đó gửi đến client bằng Socket.IO.

Dữ liệu trao đổi qua Socket.IO có thể được mã hóa bằng giao thức HTTPS. Trong ví dụ này, tôi dùng chứng chỉ SSL miễn phí của Let’s Encrypt. Lưu ý là thư mục lưu chứng chỉ của Let’s Encrypt được phân quyền chỉ cho user root truy cập.

~/astereport/index.js

/**
 * References:
 *   https://www.tutorialspoint.com/socket.io/index.htm
 *   https://github.com/pipobscure/NodeJS-AsteriskManager
 *   https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+AMI+Events
 *
 * TODO
 *   Each Extension has an individual namespace.
 */
const port    = 3000;
const fs      = require("fs");
const app     = require("express")();
const server  = require("http").Server(app);

// Socket IO - listening on port 3000
const io = require("socket.io").listen(server);

io.on("connection", (socket) => {
    console.log("A user has connected");

    socket.on("disconnect", () => {
        console.log("A user has disconnected");
    });
});

server.listen(port, () => console.log("astereport.js is listening on *:" + port));

// Present an Intro page when accessing https://example.com
app.get("/", (request, response) => response.sendFile(__dirname + "/index.html"));

// Asterisk Manager
const ami = require("asterisk-manager")(
        "5038", "localhost",
        "admin", "password",
        true);

ami.keepConnected();

// on 'dial' event occurs when there is an incoming or outgoing call
ami.on("dial", (evt) => {
    var phone    = evt.calleridnum;
    var ext      = evt.dialstring;
    var subevt   = evt.subevent;
    var uniqueid = evt.uniqueid;
    var data     = {};

    if (isNumber(phone) && subevt === "Begin" ) {
        console.log(evt);

        if (phone.length > 6 && ext.length < 6 && isNumber(ext)) {
            // push the incoming call info to the clients
            io.sockets.emit('incoming', { phone: phone, ext: ext });
        } else {
            if (!isNumber(ext)) {
                var channel = evt.channel;
                var n_bg, n_ed;

                if (-1 == channel.indexOf("@")) {
                    n_bg = channel.indexOf("/");
                    n_ed = channel.indexOf("-");
                } else {
                    n_bg = channel.indexOf("@");
                    n_ed = channel.indexOf("-");
                }
                ext   = channel.substring(n_bg + 1, n_ed - n_bg + 3);
                phone = evt.connectedlinenum;
                // push the incoming call info to the clients
                io.sockets.emit('outgoing', { phone: phone, ext: ext });
            }
        }
    }
});

// Check if the input `n` is a valid number
function isNumber(n) {
    return !isNaN(parseFloat(n)) && isFinite(n);
}

Dùng NGINX làm reverse proxy + Let’s Encrypt để mã hóa thông tin

T.B.C

Cài đặt & khởi động

cd ~/astereport
pm2 start index.js
pm2 startup

Xử lý sự kiện ở client

Phía client nhận sự kiện real-time từ Socket.IO rất đơn giản như sau:

Bước 1: Liên kết thư viện client JavaScript của socket.io, như dưới đây tôi tải qua CDN.

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.3.0/socket.io.slim.js"></script>

Bước 2: Xử lý sự kiện khi có cuộc gọi đến / đi bằng JavaScript.

const socket = io("http://example.com:3000");

// There is an outgoing call
socket.on("outgoing", (data) => {
    console.log("outgoing call: " + data.ext + " -> " + data.phone);
});

// There is an incoming call
socket.on('incoming', (data) => {
    console.log("incoming call: " + data.phone + " -> " + data.ext);
});

Phản hồi về bài viết

Cùng thảo luận chút nhỉ!

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.