From 3e9e3cb9c7d0f62983505e10330ab5a42d5ba803 Mon Sep 17 00:00:00 2001 From: Stefan Ritter Date: Mon, 31 Jul 2023 14:50:26 +0200 Subject: [PATCH] First running version --- .gitignore | 1 + LICENSE | 13 +++++ README.md | 1 + config.php.dist | 15 ++++++ css/gideonstar.css | 59 +++++++++++++++++++++++ img/favicon.png | Bin 0 -> 1795 bytes index.php | 22 +++++++++ js/app.js | 117 +++++++++++++++++++++++++++++++++++++++++++++ locale/de.php | 12 +++++ locale/en.php | 12 +++++ php/ami_get.php | 78 ++++++++++++++++++++++++++++++ php/ami_set.php | 46 ++++++++++++++++++ 12 files changed, 376 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 config.php.dist create mode 100644 css/gideonstar.css create mode 100644 img/favicon.png create mode 100644 index.php create mode 100644 js/app.js create mode 100644 locale/de.php create mode 100644 locale/en.php create mode 100644 php/ami_get.php create mode 100644 php/ami_set.php 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 0000000000000000000000000000000000000000..a8f7f168b158062660b98efc9d9d0c5185a38d23 GIT binary patch literal 1795 zcmV+e2mJVnP)HWz@X%Cz)2*GgMn8Pc#>`c zx`pJ>16YuQfcx2FlB0DT2z-_x@%ut>5F$O1gCGalQrp27eeFvr5T6{t@?< z6ktM1q;~*)eK^zrf8`*sVeMDoNE^ZL32aJ{_+lR(t8)-kQ(Iv}fb&u&y&c$B;c!TW zJ^fv_3+NwLfQK?5K2qUuc@Bb^>560@xGaN2)-8Ii!z4A-Rc~rM-(Tz%RgfU;uDT z3U<0IHVezIZ_x!P<=n*E;shW&0Y*6i$n*^m^|bB{pn?jv+&2KL0*Fd#Cji;K0iwLp z2|#uN%rCkCzXMZ&Q{<3ukX(|w+BI|0}exVtS0@Q4$D zOa)k8bO9PQZ5gKciVv_K1{$np^)cWlwpbNffJK_NOjCSUX?)d^_%|%=p%8_}g%)6! z++{OKvAg6JD0OJLT<%RP_niy0d;In)xv*aDJIgRe%Ze^QDd(me`Z@tfUI1Me=>#Bo z0q!rl0DsDq!5GD+%jHUSm;OB-zSjcVe4gGq#dosdF4ESb3(%rz%M^ocq!%l`Vxzhr z&NSfHTyy~%GDt<3%mZ|D4^KD&usRdI+6h3W0*DEodjrU>3#u)87u2e0%T7012r8z&nF$d%p)B=$K!h;>}ij5^h|j^MHp^tszHE-nzl4~ z1k?}X72nr2zN4b71$2|cr(1xD@^q0R2+)}0B}uV(^AGk1UiH{MGpqmtG~{?Dd`Zv= zRN`pi{hR=a32UhkFv zfk_`)iN(219>ux!9_fXh@`Nq5ICq@KD%WnA;!uloF9N|4994m~=rlE^08fB?^aUbI^svvDtA1$l{iYxeA9xIS67LJ2s90Y1|u7 zQcS%HWL6G>7}fqW{EQ;N^bAN3P&f?EK_H{rxVQrJPw{4=lI-$Sh*M|3%|S3tZIj>W zC?*0R&pu9z^jI&#vRtrKVRo5MTZ{$hFK3uZ6JD#iO{r(d8E0PXP2J5>s&;mro;=!m+SpK=gnKWmryB9agQc{cxM%dhHD zm_dw^*5x4Bs%;4SAjJ^?xo9&p#_bxNz}vuRqNBU~$gN9xl;&lkTfD>MKFPQOltZlY zTp`oCZJOVGSEh;UfFrPre)4>WX>v1O8g{4re`iOzBgUox<`yj?dX{Oxkl#=#JxufE=6C!vYGFV}4yV&G_Csi6?-WwI^ut + + + + + + 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 @@ +