commit 3e9e3cb9c7d0f62983505e10330ab5a42d5ba803 Author: Stefan Ritter Date: Mon Jul 31 14:50:26 2023 +0200 First running version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f4773f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +config.php diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..90107a0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,13 @@ + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + + Copyright (C) 2004 Sam Hocevar + 14 rue de Plaisance, 75014 Paris, France + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHAT THE FUCK YOU WANT TO. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d31e3d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Asterisk Queue Manager diff --git a/config.php.dist b/config.php.dist new file mode 100644 index 0000000..9d37a3a --- /dev/null +++ b/config.php.dist @@ -0,0 +1,15 @@ + diff --git a/css/gideonstar.css b/css/gideonstar.css new file mode 100644 index 0000000..d966c73 --- /dev/null +++ b/css/gideonstar.css @@ -0,0 +1,59 @@ +:root { + --red: #ff595e; + --green: #8ac926; + --yellow: #ffca3a; + --blue: #1982c4; + --purple: #6a4c93; +} + +* { + margin: 0; + padding: 0; +} + +body { + background: black; + color: white; + font-family: Tahoma; + font-size: 14px; +} + +.offline { + background: var(--red); +} + +.online { + background: var(--green); +} + +h2 { + color: var(--yellow); + font-size: 1.2em; + margin: 10px; +} + +ul { + display: flex; + flex-wrap: wrap; + flex-direction: row; + list-style-type: none; + margin-left: 10px; + margin-top: 10px; +} + +ul li.box { + display: flex; + align-items: center; + justify-content: center; + width: 72px; + height: 72px; + margin: 2px; + font-weight: bold; + cursor: pointer; + +} + +ul li:hover { + outline: 1px solid white; + font-size: 1.1em; +} diff --git a/img/favicon.png b/img/favicon.png new file mode 100644 index 0000000..a8f7f16 Binary files /dev/null and b/img/favicon.png differ diff --git a/index.php b/index.php new file mode 100644 index 0000000..6e85986 --- /dev/null +++ b/index.php @@ -0,0 +1,22 @@ + + + + + + + Asterisk Queue Manager + + + + + +

:

+ + +

:

+ + +

:

+ + + diff --git a/js/app.js b/js/app.js new file mode 100644 index 0000000..0cfe809 --- /dev/null +++ b/js/app.js @@ -0,0 +1,117 @@ +var baseurl = (window.location.origin); + +function get_data() { + var xhr = new XMLHttpRequest(); + xhr.open('GET', baseurl + '/php/ami_get.php', true); + xhr.onreadystatechange = function() { + if(xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { + var response = JSON.parse(xhr.responseText); + + // + // SIPpeers + // + + var sippeers = response.sippeers; + var dom_sippeers = document.getElementById('peers'); + + // Clear ul + dom_sippeers.innerHTML = ''; + + for(var item in sippeers) { + + // Create li + var node = document.createElement('li'); + + // Add online or offline class + if(sippeers[item] == '0') { + node.classList.add('offline'); + } + if(sippeers[item] == '1') { + node.classList.add('online'); + } + + // It's a box + node.classList.add('box'); + + // Add text + var text = document.createTextNode(item); + node.appendChild(text); + + // Add attributes + node.setAttribute('section', 'sippeers'); + node.setAttribute('value', item); + + // Make it clickable + node.addEventListener('click', function() { + var section = this.getAttribute('section'); + var value = this.getAttribute('value'); + + ami_set(section, value); + }); + + // Add li to ul + dom_sippeers.appendChild(node); + } + + // + // QueueMembers + // + + var queuemembers = response.queuemembers; + var dom_queuemembers = document.getElementById('queuemembers'); + + // Clear ul + dom_queuemembers.innerHTML = ''; + + // Asterisk sorts the members the wrong way round + queuemembers.reverse(); + + for(var item in queuemembers) { + + // Create li + var node = document.createElement('li'); + + // Queue members are always green (TODO: really?) + node.classList.add('online'); + + // It's a box + node.classList.add('box'); + + // Add text + var text = document.createTextNode(queuemembers[item]); + node.appendChild(text); + + // Add attributes + node.setAttribute('section', 'queuemembers'); + node.setAttribute('value', queuemembers[item]); + + // Make it clickable + node.addEventListener('click', function() { + var section = this.getAttribute('section'); + var value = this.getAttribute('value'); + + ami_set(section, value); + }); + + // Add li to ul + dom_queuemembers.appendChild(node); + } + } + }; + + xhr.send(); +} + +function ami_set(section, value) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', baseurl + '/php/ami_set.php?s=' + section + '&v=' + value, true); + xhr.send(null); + get_data(); +} + +// Run it once, because setInterval executes it _after_ the delay +get_data(); + +// Run every second +setInterval(get_data, 1000); + diff --git a/locale/de.php b/locale/de.php new file mode 100644 index 0000000..edbb4c2 --- /dev/null +++ b/locale/de.php @@ -0,0 +1,12 @@ + 'Telefone', + 'agent' => 'Agent', + 'agents' => 'Agenten', + 'call' => 'Anruf', + 'calls' => 'Anrufe', + 'in_queue' => 'in der Warteschlange' +]; + +?> diff --git a/locale/en.php b/locale/en.php new file mode 100644 index 0000000..cccc432 --- /dev/null +++ b/locale/en.php @@ -0,0 +1,12 @@ + 'Phones', + 'agent' => 'Agent', + 'agents' => 'Agents', + 'call' => 'Call', + 'calls' => 'Calls', + 'in_queue' => 'in Queue' +]; + +?> diff --git a/php/ami_get.php b/php/ami_get.php new file mode 100644 index 0000000..d4e4cea --- /dev/null +++ b/php/ami_get.php @@ -0,0 +1,78 @@ + $sippeers +// + +$sippeers = []; +$extension = ''; +$extension_status = 0; + +fputs($socket, "Action: SIPpeers\r\n\r\n"); +while(($data = fgets($socket, 8096)) != "Event: PeerlistComplete\r\n") { + $pattern_extension = '/^ObjectName:\s(\d\d\d).*$/'; + if(preg_match_all($pattern_extension, $data, $match)) { + $extension = $match[1][0]; + } + + $pattern_ipaddress = '/^IPaddress.*$/'; + if(preg_match($pattern_ipaddress, $data)) { + $pattern_onlinestatus = '/^IPaddress:\s(-none-).*$/'; + if(preg_match_all($pattern_onlinestatus, $data, $match)) { + $extension_status = '0'; + } else { + $extension_status = '1'; + } + + $sippeers[$extension] = $extension_status; + } +} + +// +// Action: QueueStatus -> $queuemembers +// +// TODO: Status: x +// 2 In Use +// 6 Ringing + +$queuemembers = []; + +fputs($socket, "Action: QueueStatus\r\n"); +fputs($socket, "Queue: warteschlange\r\n\r\n"); +while(($data = fgets($socket, 8096)) != "Event: QueueStatusComplete\r\n") { + $pattern_name = '/^Name.*$/'; + if(preg_match($pattern_name, $data)) { + $pattern_extension = '/^Name:\sSIP\/(\d\d\d).*$/'; + if(preg_match_all($pattern_extension, $data, $match)) { + array_push($queuemembers, $match[1][0]); + } + } +} + +// Close socket +fclose($socket); + +// +// Create JSON data package +// + +echo json_encode( + array( + 'sippeers' => $sippeers, + 'queuemembers' => $queuemembers, + ), +); + +?> diff --git a/php/ami_set.php b/php/ami_set.php new file mode 100644 index 0000000..806d244 --- /dev/null +++ b/php/ami_set.php @@ -0,0 +1,46 @@ +