<?php

/**
 * Класс для работы с Asterisk по протоколу AMI через сокет.
 * Реализовано: подключение, авторизация, отправка команд, отправка СМС, отключение.
 * Успешно работало на проде в связке с Yeastar TG400.
 * 
 * Class to work with Asterisk gateway throuth AMI protocol via socket.
 * Features: connection, authorization, send commands, send SMS, disconnect.
 * Successfully used in production with Yeastar TG400.
 *
 * @see https://habr.com/post/253387/
 */
class AMI {
    
    /** string Параметры подключения к Asterisk. Asterisk config.  */
    private const AMI_HOST = '192.168.1.19';
    private const AMI_PORT = 5038;
    private const AMI_USER = 'user';
    private const AMI_PASS = 'useruser';

    /** int Код последней операции. Last action ID. */
    public $lastActionID = 0;

    /** array Массив строк результата последней операции. Array of strings with result of last action. */
    public $lastRead = [];

    /** resource Подключение к сокету. Connection socket. */
    private $connection;

    /** array Описывает данные о последней СМС. Describes last SMS data. */
    private $sms_data = [];
    
    /**
     * AMI constructor.
     */
    function __construct() {
        $this->connect();
    }
    
    /**
     * Соединяет со шлюзом.
     * Connects with gateway.
     *
     * @return bool
     */
    public function connect() {
        $this->connection = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($this->connection === FALSE) {
            echo "Cannot socket_create(): ".socket_strerror(socket_last_error())."\n";
            return FALSE;
        }
        if (socket_connect($this->connection, self::AMI_HOST, self::AMI_PORT) === FALSE) {
            echo "Cannot socket_connect(): ".socket_strerror(socket_last_error())."\n";
            return FALSE;
        }
        return TRUE;
    }
    
    /**
     * Авторизирует на шлюзе.
     * Authorizes on gateway.
     *
     * @return bool
     */
    public function auth(): bool {
        $command = [
            "Action: Login",
            "Username: ".self::AMI_USER,
            "Secret: ".self::AMI_PASS,
        ];
        $this->command($command);
        return $this->lastRead["Response"] === "Success";
    }
    
    /**
     * Пишет данные в сокет.
     * Writes data into socket.
     *
     * @param array $command
     * @return int
     */
    public function write(array $command): int {
        $this->lastActionID = rand(10000000000000000, 99999999900000000);
        $string = implode("\r\n", $command);
        socket_write($this->connection, "ActionID: {$this->lastActionID}\r\n{$string}\r\n\r\n");
        return $this->lastActionID;
    }
    
    /**
     * Читает данные из сокета.
     * Reads data from socket.
     *
     * @return array
     */
    public function read(): array {
        $result = [];
        $data = socket_read($this->connection, 1024);
        $data = str_replace("Asterisk Call Manager/1.1\r\n", '', $data);
        $data = explode("\r\n", trim($data));
        foreach ($data as $key => $string) {
            $pos = strpos($string, ':');
            $key = substr($string, 0, $pos);
            $string = substr($string, $pos + 2, strlen($string));
            $result[$key] = $string;
        }
        return $this->lastRead = $result;
    }
    
    /**
     * Отправляет команду шлюзу и возвращает ответ.
     * Sends command throuth socket and returns response.
     *
     * @param array $command
     * @return array
     */
    public function command(array $command): array {
        $this->write($command);
        return $this->read();
    }
    
    /**
     * Отправляет СМС.
     * Sends SMS.
     *
     * @param string $phone_number
     * @param string $sms_text
     * @param int    $sim_id
     * @return int SMS ID
     */
    public function sendSMS(int $dispatch_id, string $phone_number, string $sms_text, int $sim_id): int {
        $this->sms_data['sim_id'] = $sim_id;
        $this->sms_data['phone_number'] = $phone_number;
        $this->sms_data['text'] = $sms_text;
        $this->sms_data['date_send'] = date('Y-m-d H:i:s');
        // $this->sms_data['date_status'] = "NULL";
        $this->sms_data['status'] = 0;
        $smsid = $this->writeSmsReport();
        $command = [
            "Action: smscommand",
            "command: gsm send sms ".($sim_id + 1)." {$phone_number} \"".urlencode($sms_text)."\" ID{$smsid}",
        ];
        $this->command($command);
        return $smsid;
    }
    
    /**
     * Отключает от шлюза и закрывает сокет.
     * Disconnects from gateway and closes socket.
     */
    public function disconnect() {
        if ($this->connection) {
            $this->write(["Action: Logoff"]);
            socket_close($this->connection);
        }
    }
}