namespace Asterisk; use PAMI\Client\Impl\ClientImpl as PamiClient; use PAMI\Message\Event\EventMessage; use PAMI\Message\Event\HangupEvent; use PAMI\Message\Event\NewstateEvent; use PAMI\Message\Event\OriginateResponseEvent; use PAMI\Message\Action\OriginateAction; use React\EventLoop\Factory; class AsteriskDaemon { private $asterisk; private $server; private $loop; private $interval = 0.1; private $retries = 10; private $options = array( 'host' => 'host', 'scheme' => 'tcp://', 'port' => 5038, 'username' => 'user', 'secret' => ' password', 'connect_timeout' => 10000, 'read_timeout' => 10000 ); private $opened = FALSE; private $runned = FALSE; public function __construct(Server $server) { $this->server = $server; $this->asterisk = new PamiClient($this->options); $this->loop = Factory::create(); $this->asterisk->registerEventListener(new AsteriskEventListener($this->server), function (EventMessage $event) { return $event instanceof NewstateEvent || $event instanceof HangupEvent; }); $this->asterisk->open(); $this->opened = TRUE; $asterisk = $this->asterisk; $retries = $this->retries; $this->loop->addPeriodicTimer($this->interval, function () use ($asterisk, $retries) { try { $asterisk->process(); } catch (Exception $exc) { if ($retries-- <= 0) { throw new \RuntimeException('Exit from loop', 1, $exc); } sleep(10); } }); } public function __destruct() { if ($this->loop && $this->runned) { $this->loop->stop(); } if ($this->asterisk && $this->opened) { $this->asterisk->close(); } } public function run() { $this->runned = TRUE; $this->loop->run(); } public function getLoop() { return $this->loop; } }
namespace Asterisk; use PAMI\Message\Event\EventMessage; use PAMI\Listener\IEventListener; use PAMI\Message\Event\NewstateEvent; use PAMI\Message\Event\HangupEvent; use PAMI\Message\Event\OriginateResponseEvent; class AsteriskEventListener implements IEventListener { private $server; public function __construct(Server $server) { $this->server = $server; } public function handle(EventMessage $event) { // getChannelState 6 = Up getChannelStateDesc() // TODO BridgeEnterEvent if ($event instanceof NewstateEvent && $event->getChannelState() == 6) { $client = $this->server->getClientById($event->getCallerIDNum()); if (!$client) { return; } $client->setMessage($event); // TODO BridgeLeaveEvent } elseif ($event instanceof HangupEvent) { $client = $this->server->getClientById($event->getCallerIDNum()); if (!$client) { return; } $client->setMessage($event); } } }
namespace Asterisk; use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; class Server implements MessageComponentInterface { /** * * @var SplObjectStorage */ private $clients; /** * asterisk * @var AsteriskDaemon */ private $daemon; public function __construct() { $this->clients = new \SplObjectStorage; $this->daemon = new AsteriskDaemon($this); } function getLoop() { return $this->daemon->getLoop(); } public function onOpen(ConnectionInterface $conn) { //echo "Open\n"; } public function onMessage(ConnectionInterface $from, $msg) { //echo "Message\n"; $json = json_decode($msg); if (json_last_error()) { echo "Json error: " . json_last_error_msg() . "\n"; return; } switch ($json->Action) { case 'Register': //echo "Register client\n"; $client = $this->getClientById($json->Id); if ($client) { if ($client->getConnection() != $from) { $client->setConnection($from); } $client->process(); } else { $this->clients->attach(new Client($from, $json->Id)); } break; default: break; } } public function onClose(ConnectionInterface $conn) { //echo "Close\n"; $client = $this->getClientByConnection($conn); if ($client) { $client->closeConnection(); } } public function onError(ConnectionInterface $conn, \Exception $e) { echo "Error: " . $e->getMessage() . "\n"; $client = $this->getClientByConnection($conn); if ($client) { $client->closeConnection(); } } /** * * @param ConnectionInterface $conn * @return \Asterisk\Client or NULL */ public function getClientByConnection(ConnectionInterface $conn) { $this->clients->rewind(); while($this->clients->valid()) { $client = $this->clients->current(); if ($client->getConnection() == $conn) { //echo "Client found by connection\n"; return $client; } $this->clients->next(); } return NULL; } /** * * @param string $id * @return \Asterisk\Client or NULL */ public function getClientById($id) { $this->clients->rewind(); while($this->clients->valid()) { $client = $this->clients->current(); if ($client->getId() == $id) { //echo "Client found by id\n"; return $client; } $this->clients->next(); } return NULL; } }
namespace Asterisk; use Ratchet\ConnectionInterface; use PAMI\Message\Event\EventMessage; use PAMI\Message\Event\NewstateEvent; use PAMI\Message\Event\HangupEvent; use PAMI\Message\Event\OriginateResponseEvent; class Client { /** * * @var PAMI\Message\Event\EventMessage */ private $message; /** * * @var Ratchet\ConnectionInterface */ private $connection; /** * * @var string */ private $id; /** * . * @var int */ private $lastactive; public function __construct(ConnectionInterface $connection, $id=NULL) { $this->connection = $connection; if ($id) { $this->id = $id; } $this->lastactive = time(); } function getConnection() { return $this->connection; } function setConnection($connection) { $this->connection = $connection; } function closeConnection() { $this->connection->close(); $this->connection = NULL; } public function getMessage() { return $this->message; } public function setMessage(EventMessage $message) { $this->message = $message; $this->process(); } public function process() { if (!$this->connection || !$this->message) { return; } if ($this->message instanceof NewstateEvent) { $message = array('event' => 'incoming', 'value' => $this->message->getConnectedLineNum()); } elseif ($this->message instanceof HangupEvent) { $message = array('event' => 'hangup'); } else { return; } $json = json_encode($message); $this->connection->send($json); } function getId() { return $this->id; } function setId($id) { $this->id = $id; } }
require_once implode(DIRECTORY_SEPARATOR, array(__DIR__ , 'vendor', 'autoload.php')); //use Ratchet\Server\EchoServer; use Asterisk\Server; try { $server = new Server(); $app = new Ratchet\App('192.168.0.241', 8080, '192.168.0.241', $server->getLoop()); $app->route('/asterisk', $server, array('*')); $app->run(); } catch (Exception $exc) { $error = "Exception raised: " . $exc->getMessage() . "\nFile: " . $exc->getFile() . "\nLine: " . $exc->getLine() . "\n\n"; echo $error; exit(1); }
function Asterisk(address, phone) { var delay = 3000; var isIdle = true, isConnected = false; var content = $('<div/>', {id: 'asterisk-content', style: 'text-align: center;'}); var widget = $('<div/>', {id: 'asterisk-popup', class: 'popup-box noprint', style: 'min-height: 180px;'}) .append($('<div/>', {class: 'header', text: ''})) .append(content).hide(); var input = $('#popup-addorder').find('input[name=phone]'); var client = connect(address, phone); $('body').append(widget); function show() { widget.stop(true).show(); }; function hide() { widget.show().delay(delay).fadeOut(); }; function connect(a, p) { if (!a || !p) { console.log('Asterisk: no address or phone'); return null; } var ws = new WebSocket('wss://' + a + '/wss/asterisk'); ws.onopen = function() { isConnected = true; this.send(JSON.stringify({Action: 'Register', Id: p})); }; ws.onclose = function() { isConnected = false; content.html($('<p/>', {text: ''})); hide(); }; ws.onmessage = function(evt) { var msg = JSON.parse(evt.data); if (!msg || !msg.event) { return; } switch (msg.event) { case 'incoming': var p = msg.value; content.html($('<p/>').html('<br>' + p)) .append($('<p/>').html($('<a/>', {href: '?module=clients&search=' + p, class: 'button'}) .html($('<img/>', {src: '/images/icons/find.png'})).append(' '))); input.val(p); show(); isIdle = false; break; case 'hangup': if (!isIdle) { content.html($('<p/>', {text: ''})); hide(); isIdle = true; } break; default: console.log('Unknown event' + msg.event); } }; ws.onerror = function(evt) { content.html($('<p/>', {text: ''})); hide(); console.log('Asterisk: error', evt); }; return ws; }; };
Source: https://habr.com/ru/post/311986/
All Articles