<?php\r
-//V1.0\r
+//V1.1\r
require_once(__DIR__.DIRECTORY_SEPARATOR.'baseagi.php');\r
\r
-class AGI extends baseAGI\r
+trait funcAGI\r
{\r
- public function __construct()\r
- {\r
- parent::__construct();\r
- }\r
\r
//подготовка параметров для передачи\r
protected function make_params($inparams)\r
//Hangup a channel.\r
public function Hangup($channelname = NULL)\r
{\r
+ $params = $this->make_params(get_defined_vars());\r
$cmd = 'hangup';\r
$process_result = $this->ProcessCmd($cmd.$params);\r
if ($process_result['code'] == 200)\r
return FALSE; \r
} \r
}\r
+\r
+class AGI \r
+{\r
+ use stdioAGI, baseAGI, funcAGI;\r
+}\r
+\r
+class asyncAGI \r
+{\r
+ use callioAGI, baseAGI, funcAGI\r
+ {\r
+ callioAGI::__construct insteadof baseAGI;\r
+ baseAGI::__construct as subconstruct;\r
+ }\r
+}
\ No newline at end of file
<?php
-//V1.0
+//V1.1
/*
require_once(__DIR__.DIRECTORY_SEPARATOR.'common.php');
require_once(__DIR__.DIRECTORY_SEPARATOR.'baseami.php');
require_once(__DIR__.DIRECTORY_SEPARATOR.'timer.php');
+require_once(__DIR__.DIRECTORY_SEPARATOR.'agi.php');
+
class AMI extends baseAMI
{
protected $semaphores = [];
protected $TMP = [];
-
+ protected $AGIs = [];
+
+
//конструктор установка параметров
public function __construct($config = [])
{
}
}
+ $this->add_event_handler('AsyncAGIStart', [$this, 'AsyncAGIStartHdl']);
+ $this->add_event_handler('AsyncAGIEnd', [$this, 'AsyncAGIEndHdl']);
+ $this->add_event_handler('AsyncAGIExec', [$this, 'AsyncAGIExecHdl']);
}
//генерирует MD5 challenge для аутентификации (не понятно где применять)
}
}
- //Ð\9dабоÑ\80 меÑ\82одов длÑ\8f Ñ\80абоÑ\82Ñ\8b Ñ\81о вÑ\81Ñ\82Ñ\80оенной Ð\91Ð\94 asterisk
- public function AGI($Channel, $Command, $CommandID = NULL)
+ //оÑ\82пÑ\80авка коммандÑ\8b asyncAGI
+ public function SendAsyncAGICmd($Channel, $Command, $CommandID = NULL)
{
$params = $this->make_params(get_defined_vars());
$ActId = $this->send_action('AGI', $params);
LOG::log(__METHOD__.' '.$response['Message'], 5);
if ($response['Response'] == 'Success')
{
+ //$this->SetEventHook($ActionID, $Callback);
+
return TRUE;
}
else
LOG::log(__METHOD__.' '.$response['Message'], 5);
if ($response['Response'] == 'Success')
{
- return $response['ActionID'];
+ $retevent = new AMIEvent();
+ $this->SetEventHook($response['ActionID'], [$retevent, 'SetData']);
+ return $retevent;
}
else
{
}
return $retval;
}
+
+ //обработчик запуска asyncagi
+ protected function AsyncAGIStartHdl($event_name, $event)
+ {
+ $this->AGIs[$event['Channel']] = new asyncAGI($event['Env'], $this);
+ }
+
+ //обработчик завершения asyncagi
+ protected function AsyncAGIEndHdl($event_name, $event)
+ {
+ $this->AGIs[$event['Channel']]->stop();
+ unset($this->AGIs[$event['Channel']]);
+ }
+
+ //обработчик входного буфера asyncAGI
+ public function AsyncAGIExecHdl($event_name, $event)
+ {
+ $this->AGIs[$event['Channel']]->PutToBuffer($event['Result']);
+ }
+
+
+ //получить instance asyncAGI
+ public function GetAsyncAGIInstance($channel)
+ {
+ if (isset($this->AGIs[$channel]))
+ {
+ return $this->AGIs[$channel];
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ //получить список каналов с запущенными AsyncAGI
+ public function GetAsyncAGIChannelList()
+ {
+ return array_keys($this->AGIs);
+ }
+
+}
+
+class AMIEvent
+{
+ private $state = FALSE;
+
+ public function SetData($data)
+ {
+ if (!$this->state)
+ {
+ foreach ($data as $var => $value)
+ {
+ $this->$var = $value;
+ }
+ $this->state = TRUE;
+ }
+ }
+
+ public function IsReady()
+ {
+ return $this->state;
+ }
+
+ public function WaitUntilReady()
+ {
+ while (!$this->state)
+ {
+ usleep(100000);
+ }
+ }
}
\ No newline at end of file
<?php\r
-//V1.0\r
-class baseAGI\r
+//V1.1\r
+trait stdioAGI\r
+{\r
+\r
+ protected function RxData()\r
+ {\r
+ $line = stream_get_line (STDIN , 1500, PHP_EOL); //получение сырых данных с парсингом по переводу строк\r
+ return $line;\r
+\r
+ }\r
+\r
+ protected function TxData($cmd)\r
+ {\r
+ fwrite(STDOUT, $cmd.PHP_EOL);\r
+ }\r
+\r
+\r
+}\r
+\r
+trait callioAGI\r
+{\r
+ protected $RxBuffer = [];\r
+ protected $AmiInstance;\r
+ \r
+ public function __construct($UriEncodedRequest,$AmiInstance)\r
+ {\r
+ $this->AmiInstance = &$AmiInstance;\r
+ $this->PutToBuffer($UriEncodedRequest);\r
+ $this->subconstruct();\r
+ }\r
+ public function stop()\r
+ {\r
+ unset($this->AmiInstance);\r
+ $this->state = FALSE;\r
+ }\r
+\r
+ public function PutToBuffer($uriencline)\r
+ {\r
+ $uriencline = str_replace('%FFFFFF', '%', $uriencline);\r
+ $lines = rawurldecode($uriencline);\r
+ $lines = explode("\n", $lines); \r
+ array_pop($lines);\r
+ foreach ($lines as $line)\r
+ {\r
+ $this->RxBuffer[] = $line;\r
+ }\r
+ }\r
+ \r
+ protected function RxData()\r
+ {\r
+ while (true)\r
+ {\r
+ $line = array_shift($this->RxBuffer);\r
+ if ($line === NULL)\r
+ {\r
+ usleep(250000);\r
+ }\r
+ else\r
+ {\r
+ return $line;\r
+ }\r
+ }\r
+\r
+ }\r
+\r
+ protected function TxData($cmd)\r
+ {\r
+ $this->AmiInstance->SendAsyncAGICmd($this->GetRequest('agi_channel'), $cmd);\r
+ return TRUE;\r
+ }\r
+}\r
+\r
+trait baseAGI\r
{\r
protected $request = FALSE;\r
protected $last_response = NULL;\r
-\r
+ protected $state = FALSE;\r
\r
public function __construct()\r
{\r
+ \r
$this->request = $this->ProcessRequest();\r
if ($this->request === FALSE)\r
{\r
- return FALSE;\r
+ return;\r
}\r
+ $this->state = TRUE;\r
+ }\r
+ \r
+ public function IsAlive()\r
+ {\r
+ return $this->state;\r
}\r
//получение запроса\r
protected function ProcessRequest()\r
{\r
while(TRUE)\r
{\r
- $line = stream_get_line (STDIN , 1500, PHP_EOL); //получение сырых данных с парсингом по переводу строк\r
+ $line = $this->RxData();\r
if ($line === ''){break;} //пустая строка означает конец пакета\r
if ($line === FALSE){return FALSE;} //false означает осутствие данных\r
$parse_result = preg_match('/(^.[^ ]*): (.*)/', $line, $parsed_line);\r
//обработка комманды\r
protected function ProcessCmd($cmd)\r
{\r
- fwrite(STDOUT, $cmd.PHP_EOL);\r
- $line = stream_get_line (STDIN , 1500, PHP_EOL); //получение сырых данных с парсингом по переводу строк\r
+ if (!$this->IsAlive())\r
+ {\r
+ return FALSE;\r
+ }\r
+\r
+ $this->TxData($cmd);\r
+ $line = $this->RxData();\r
$parse_result = preg_match('/(\d+)(?:.)(.*)/', $line, $parsed_line);\r
if ($parse_result === 1)\r
{\r
<?php
-//V1.0
+//V1.1.0
class baseAMI
{
protected $conn_handle = FALSE;
protected $events = [];
protected $responses = [];
protected $event_handlers = [];
+ protected $event_hooks = [];
protected $refresh_lock = FALSE;
//конструктор, настройка по умолчанию
{
foreach ($this->events as $index => $event)
{
-
$event_name = strtolower($event['Event']);
+ if (isset($event['ActionID']))
+ {
+ if (isset($this->event_hooks[$event['ActionID']]))
+ {
+ if (is_callable($this->event_hooks[$event['ActionID']]))
+ {
+ LOG::log("Hooked event '${event_name}' with ActionID '{$event['ActionID']}'.",5);
+ call_user_func($this->event_hooks[$event['ActionID']], $event);
+ unset($this->event_hooks[$event['ActionID']]);
+ }
+ }
+ }
if (isset($this->event_handlers[$event_name]))
{
$run_handler = $this->event_handlers[$event_name];
else
{
LOG::log("Got event '${event_name}', runing '${run_handler_name}' handler for processing it.",5);
- $ret_h_data = call_user_func($run_handler, $event_name, $event);
+ call_user_func($run_handler, $event_name, $event);
}
unset($this->events[$index]);
}
}
+ //установить hook на событие по ActionID
+ public function SetEventHook($ActionID, $Callback)
+ {
+ if (is_callable($Callback))
+ {
+ $this->event_hooks[$ActionID] = $Callback;
+ }
+ }
+
+ //снять hook на событие по ActionID
+ public function UnsetEventHook($ActionID)
+ {
+ unset($this->event_hooks[$ActionID]);
+ }
+
//низкоуровневая отправка запросов
protected function send_action($action,$params = [])
{
<?php
+declare (ticks=1);
+require_once 'astapilib/ami.php';
function waitanswer($a,$b)
{
var_dump($a,$b);
- var_dump(get_defined_vars());
+ var_dump(urldecode($b['Result']));
}
-echo "<pre>\n";
-require_once 'astapilib/ami.php';
+echo "<pre>\n";
+$_GET['phone'] = 3400;
var_dump($_GET);
if (!isset($_GET['phone']))
{
$AMI = new AMI(array('autorefresh' => TRUE, 'logverbose' => 6));
$is_connected = $AMI->connect("127.0.0.1", "monast", "blabla");
-$AMI->add_event_handler('originateresponse', 'waitanswer');
-$AMI->enable_events(TRUE);
if (!$is_connected)
{
exit();
}
-var_dump($AMI->Originate("Local/{$_GET['phone']}@c-2", NULL, NULL, NULL, 'AGI', 'agi:async', 30, 3500, NULL, NULL, NULL, NULL, 'azaza', 'ololo'));
+$OriginateResponce = $AMI->Originate("Local/{$_GET['phone']}@c-2", NULL, NULL, NULL, 'AGI', 'agi:async', 30, 3500, NULL, NULL, NULL, NULL);
+$OriginateResponce->WaitUntilReady();
+$channel = $OriginateResponce->Channel;
+var_dump($AMI->GetAsyncAGIChannelList());
+$AGI = $AMI->GetAsyncAGIInstance($channel);
+//$AGI = new AGI();
+echo '------------------';
+var_dump($AGI->GetVariable('CALLERID(num)'));
+var_dump($AGI->GetVariable('CALLERID(name)'));
+echo '------------------';
+var_dump($AGI->SayDigits('012345'));
+var_dump($AGI->Hangup());
+unset($AGI);
sleep(5);
-$AMI->AGI("Local/{$_GET['phone']}@c-2", 'NoOp');
\ No newline at end of file
+while (TRUE)
+{
+ sleep(1);
+ echo '+';
+}
\ No newline at end of file