First running version

This commit is contained in:
Stefan Ritter 2023-07-31 14:50:26 +02:00
commit 3e9e3cb9c7
12 changed files with 376 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
config.php

13
LICENSE Normal file
View File

@ -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.

1
README.md Normal file
View File

@ -0,0 +1 @@
# Asterisk Queue Manager

15
config.php.dist Normal file
View File

@ -0,0 +1,15 @@
<?php
// See README.md for asterisk ami configuration
$ami_host = '127.0.0.1';
$ami_port = '5038';
$ami_user = 'user';
$ami_pass = 'pass';
// Locales are in locale folder
$lang = "en";
// Styles are in css folder
$style = "gideonstar";
?>

59
css/gideonstar.css Normal file
View File

@ -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;
}

BIN
img/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

22
index.php Normal file
View File

@ -0,0 +1,22 @@
<?php include('config.php'); ?>
<?php include('locale/' . $lang . '.php'); ?>
<!DOCTYPE html>
<html lang="de-de">
<head>
<meta charset="utf-8">
<title>Asterisk Queue Manager</title>
<link rel="stylesheet" href="css/<?php echo $style; ?>.css">
<link rel="icon" type="image/png" href="img/favicon.png">
<script src="js/app.js"></script>
</head>
<body>
<h2><?php echo $locale['phones']; ?>:</h2>
<ul id="peers"></ul>
<h2><?php echo $locale['agents'] . ' ' . $locale['in_queue']; ?>:</h2>
<ul id="queuemembers"></ul>
<h2><?php echo $locale['calls'] . ' ' . $locale['in_queue']; ?>:</h2>
<ul id="queuecalls"></ul>
</body>
</html>

117
js/app.js Normal file
View File

@ -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);

12
locale/de.php Normal file
View File

@ -0,0 +1,12 @@
<?php
$locale = [
'phones' => 'Telefone',
'agent' => 'Agent',
'agents' => 'Agenten',
'call' => 'Anruf',
'calls' => 'Anrufe',
'in_queue' => 'in der Warteschlange'
];
?>

12
locale/en.php Normal file
View File

@ -0,0 +1,12 @@
<?php
$locale = [
'phones' => 'Phones',
'agent' => 'Agent',
'agents' => 'Agents',
'call' => 'Call',
'calls' => 'Calls',
'in_queue' => 'in Queue'
];
?>

78
php/ami_get.php Normal file
View File

@ -0,0 +1,78 @@
<?php
include('../config.php');
header('Content-Type: application/json');
// Open socket to ami server
$socket = fsockopen($ami_host, $ami_port);
// Login
fputs($socket, "Action: Login\r\n");
fputs($socket, "UserName: " . $ami_user . "\r\n");
fputs($socket, "Secret: " . $ami_pass . "\r\n\r\n");
//
// Action: SIPpeers -> $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,
),
);
?>

46
php/ami_set.php Normal file
View File

@ -0,0 +1,46 @@
<?php
include('../config.php');
// Read GET data
$section = $_GET['s'];
$value = $_GET['v'];
// Open socket to ami server
$socket = fsockopen($ami_host, $ami_port);
// Login
fputs($socket, "Action: Login\r\n");
fputs($socket, 'UserName: ' . $ami_user . "\r\n");
fputs($socket, 'Secret: ' . $ami_pass . "\r\n\r\n");
//
// Section: sippeers
//
if($section == 'sippeers') {
fputs($socket, "Action: QueueAdd\r\n");
fputs($socket, 'Interface: SIP/' . $value . "\r\n");
fputs($socket, "Queue: warteschlange\r\n\r\n");
}
//
// Section: queuemembers
//
if($section == 'queuemembers') {
fputs($socket, "Action: QueueRemove\r\n");
fputs($socket, 'Interface: SIP/' . $value . "\r\n");
fputs($socket, "Queue: warteschlange\r\n\r\n");
}
// It seems that it needs some time to execute
sleep(1);
// Close socket
fclose($socket);
?>