2020-07-09 22:17:26 +07:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace VK\API;
|
|
|
|
|
|
2020-09-26 17:28:13 +07:00
|
|
|
|
use \Exception;
|
|
|
|
|
use \VK\Core,
|
|
|
|
|
\VK\Robots\RobotAbstract;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* LongPoll
|
|
|
|
|
*
|
|
|
|
|
* @property string $key Ключ к серверу
|
|
|
|
|
* @property string $server Сервер
|
|
|
|
|
* @property string $ts Идентификатор последнего события
|
|
|
|
|
*
|
|
|
|
|
* @method public function __construct(object $robot) Инициализация
|
|
|
|
|
* @method public function get(int $wait = 25) Получить события
|
|
|
|
|
* @method public function handle(callable $function, int $wait = 25) Обработать события
|
|
|
|
|
*
|
|
|
|
|
* @see https://vk.com/dev/bots_longpoll
|
|
|
|
|
* @see https://vk.com/dev/groups.getLongPollServer
|
|
|
|
|
* @see https://vk.com/dev/groups.setLongPollSettings
|
|
|
|
|
*
|
|
|
|
|
* @package VK\API\LongPoll
|
|
|
|
|
* @author Арсен Мирзаев <red@hood.su>
|
|
|
|
|
*
|
|
|
|
|
* @todo Добавить обработку ошибок ($request['errors];)
|
|
|
|
|
*/
|
|
|
|
|
class LongPoll
|
2020-07-09 22:17:26 +07:00
|
|
|
|
{
|
2020-09-08 22:55:21 +07:00
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Робот
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* @var string
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
private RobotAbstract $robot;
|
2020-09-08 22:55:21 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Ключ к серверу
|
|
|
|
|
*
|
|
|
|
|
* @see $this->get()
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
private string $key;
|
2020-09-08 22:55:21 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Сервер (URL)
|
|
|
|
|
*
|
|
|
|
|
* @see $this->get()
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
private string $server;
|
2020-09-08 22:55:21 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Идентификатор последнего события
|
|
|
|
|
*
|
|
|
|
|
* От него отсчитываются новые, необработанные события
|
|
|
|
|
*
|
|
|
|
|
* @see $this->get()
|
|
|
|
|
*
|
|
|
|
|
* @var string
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
private string $ts;
|
2020-09-08 22:55:21 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Инициализация
|
|
|
|
|
*
|
|
|
|
|
* @param object $robot Робот
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
public function __construct(object $robot)
|
|
|
|
|
{
|
|
|
|
|
// Инициализация робота
|
|
|
|
|
if (!$robot->id) {
|
|
|
|
|
throw new Exception('Роботу необходимо задать идентификатор ВКонтакте');
|
|
|
|
|
}
|
|
|
|
|
if (!$robot->token) {
|
|
|
|
|
throw new Exception('Роботу необходимо задать токен для доступа к LongPoll');
|
|
|
|
|
}
|
|
|
|
|
if (!$robot->version) {
|
|
|
|
|
throw new Exception('Роботу необходимо задать версию используемого API ВКонтакте');
|
|
|
|
|
}
|
|
|
|
|
$this->robot = $robot;
|
|
|
|
|
|
|
|
|
|
// Остановка процессов-дубликатов
|
|
|
|
|
if (!file_exists(Core::init()->path['temp'])) {
|
|
|
|
|
// Если не существует каталога temp, то создать
|
|
|
|
|
mkdir(Core::init()->path['temp'], 0775, true);
|
|
|
|
|
}
|
|
|
|
|
if (file_exists($lock = Core::init()->path['temp'] . '/' . $this->robot->id . '_' . (int) $this->robot->session . '.longpoll')) {
|
|
|
|
|
// Если существует файл-блокировщик, то удалить его
|
|
|
|
|
unlink($lock);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-08 22:55:21 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Установить настройки
|
|
|
|
|
*
|
|
|
|
|
* Полная настройка и активация LongPoll
|
|
|
|
|
*
|
|
|
|
|
* @param bool $status = true Активация или деактивация
|
|
|
|
|
* @param string ...$params Изменяемые параметры
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
public function post(bool $status = true, string ...$params): array
|
|
|
|
|
{
|
|
|
|
|
// Инициализация настроек
|
|
|
|
|
$settings = [
|
|
|
|
|
'group_id' => $this->robot->id,
|
|
|
|
|
'access_token' => $this->robot->token,
|
|
|
|
|
'v' => $this->robot->version,
|
|
|
|
|
'api_version' => $this->robot->version
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// Установка переданных параметров
|
|
|
|
|
foreach ($params as $param) {
|
|
|
|
|
if ($param === 'group_id' || $param === 'access_token' || $param === 'v' || $param === 'api_version') {
|
|
|
|
|
// Блокировка параметров от изменения
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($status === true && !array_key_exists('enabled', $settings)) {
|
|
|
|
|
// Если запущена активация и не был передан параметр статуса LongPoll
|
|
|
|
|
|
|
|
|
|
// Установка параметра активации LongPoll
|
|
|
|
|
$settings['enabled'] = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$status = (int) $status;
|
|
|
|
|
|
|
|
|
|
if ($param === 'all') {
|
|
|
|
|
// Если передан параметр: установка ВСЕХ значений
|
|
|
|
|
$settings['message_new'] = $status;
|
|
|
|
|
$settings['message_reply'] = $status;
|
|
|
|
|
$settings['message_allow'] = $status;
|
|
|
|
|
$settings['message_deny'] = $status;
|
|
|
|
|
$settings['message_edit'] = $status;
|
|
|
|
|
$settings['message_typing_state'] = $status;
|
|
|
|
|
$settings['photo_new'] = $status;
|
|
|
|
|
$settings['audio_new'] = $status;
|
|
|
|
|
$settings['video_new'] = $status;
|
|
|
|
|
$settings['wall_reply_new'] = $status;
|
|
|
|
|
$settings['wall_reply_edit'] = $status;
|
|
|
|
|
$settings['wall_reply_delete'] = $status;
|
|
|
|
|
$settings['wall_reply_restore'] = $status;
|
|
|
|
|
$settings['wall_post_new'] = $status;
|
|
|
|
|
$settings['wall_repost'] = $status;
|
|
|
|
|
$settings['board_post_new'] = $status;
|
|
|
|
|
$settings['board_post_edit'] = $status;
|
|
|
|
|
$settings['board_post_restore'] = $status;
|
|
|
|
|
$settings['board_post_delete'] = $status;
|
|
|
|
|
$settings['photo_comment_new'] = $status;
|
|
|
|
|
$settings['photo_comment_edit'] = $status;
|
|
|
|
|
$settings['photo_comment_delete'] = $status;
|
|
|
|
|
$settings['photo_comment_restore'] = $status;
|
|
|
|
|
$settings['video_comment_new'] = $status;
|
|
|
|
|
$settings['video_comment_edit'] = $status;
|
|
|
|
|
$settings['video_comment_delete'] = $status;
|
|
|
|
|
$settings['video_comment_restore'] = $status;
|
|
|
|
|
$settings['market_comment_new'] = $status;
|
|
|
|
|
$settings['market_comment_edit'] = $status;
|
|
|
|
|
$settings['market_comment_delete'] = $status;
|
|
|
|
|
$settings['market_comment_restore'] = $status;
|
|
|
|
|
$settings['poll_vote_new'] = $status;
|
|
|
|
|
$settings['group_join'] = $status;
|
|
|
|
|
$settings['group_leave'] = $status;
|
|
|
|
|
$settings['group_change_settings'] = $status;
|
|
|
|
|
$settings['group_change_photo'] = $status;
|
|
|
|
|
$settings['group_officers_edit'] = $status;
|
|
|
|
|
$settings['user_block'] = $status;
|
|
|
|
|
$settings['user_unblock'] = $status;
|
|
|
|
|
$settings['like_add'] = $status;
|
|
|
|
|
$settings['like_remove'] = $status;
|
|
|
|
|
$settings['message_event'] = $status;
|
|
|
|
|
} else {
|
|
|
|
|
// Иначе
|
|
|
|
|
|
|
|
|
|
// Установка значения
|
|
|
|
|
$settings[$param] = $status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->robot->browser()->api('groups.setLongPollSettings', $settings);
|
|
|
|
|
}
|
2020-09-08 22:55:21 +07:00
|
|
|
|
|
|
|
|
|
/**
|
2020-09-26 17:28:13 +07:00
|
|
|
|
* Получить события
|
|
|
|
|
*
|
|
|
|
|
* @param int $wait Время ожидания новых событий (в секундах)
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
2020-09-08 22:55:21 +07:00
|
|
|
|
*/
|
2020-09-26 17:28:13 +07:00
|
|
|
|
public function get(int $wait = 25): array
|
2020-07-09 22:17:26 +07:00
|
|
|
|
{
|
2020-09-26 17:28:13 +07:00
|
|
|
|
if (empty($this->key) || empty($this->server) || empty($this->ts)) {
|
|
|
|
|
// Если не инициализирован LongPoll-сервер
|
|
|
|
|
|
|
|
|
|
// Запрос на получение доступа и данных LongPoll-сервера
|
|
|
|
|
$response = $this->robot->browser()->api('groups.getLongPollServer', [
|
|
|
|
|
'group_id' => $this->robot->id,
|
|
|
|
|
'access_token' => $this->robot->token,
|
|
|
|
|
'v' => $this->robot->version
|
|
|
|
|
])['response'];
|
|
|
|
|
|
|
|
|
|
// Ключ доступа
|
|
|
|
|
$this->key = $response['key'];
|
|
|
|
|
|
|
|
|
|
// Сервер хранящий события
|
|
|
|
|
$this->server = $response['server'];
|
|
|
|
|
|
|
|
|
|
// Идентификатор последнего события
|
|
|
|
|
$this->ts = $response['ts'];
|
2020-07-09 22:17:26 +07:00
|
|
|
|
}
|
2020-09-26 17:28:13 +07:00
|
|
|
|
|
|
|
|
|
// Запрос на получение событий
|
|
|
|
|
return $this->robot->browser()->post($this->server . '?act=a_check&key=' . $this->key . '&ts=' . $this->ts . '&wait=' . $wait);
|
2020-07-09 22:17:26 +07:00
|
|
|
|
}
|
2020-09-26 17:28:13 +07:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Обработать события
|
|
|
|
|
*
|
|
|
|
|
* Получает и обрабатывает события
|
|
|
|
|
*
|
|
|
|
|
* @param callable $function Обработка
|
|
|
|
|
* @param int $wait Время ожидания новых событий (в секундах)
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function handle(callable $function, int $wait = 25): array
|
2020-09-08 22:55:21 +07:00
|
|
|
|
{
|
2020-09-26 17:28:13 +07:00
|
|
|
|
// Файл-блокировщик и PID процесса
|
|
|
|
|
$lock = Core::init()->path['temp'] . '/' . $this->robot->id . '_' . (int) $this->robot->session . '.longpoll';
|
|
|
|
|
$pid = getmypid();
|
|
|
|
|
|
|
|
|
|
// Создание или пересоздание файла-блокировщика
|
|
|
|
|
file_put_contents($lock, $pid);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
// Выполняется пока существует файл-блокировщик
|
|
|
|
|
|
|
|
|
|
// Запрос на получение событий
|
|
|
|
|
$request = $this->get($wait);
|
|
|
|
|
|
|
|
|
|
// [ВНИМАНИЕ] Соединение будет открыто даже при создании нового процесса LongPoll
|
|
|
|
|
|
|
|
|
|
if (!file_exists($lock) || (int) fread(fopen($lock, 'r'), filesize($lock)) !== $pid) {
|
|
|
|
|
// Проверка существования файла-блокировщика и соответствие его PID
|
|
|
|
|
|
|
|
|
|
// Завершение работы
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($request['response']['updates'])) {
|
|
|
|
|
// Если получены необработанные события
|
|
|
|
|
|
|
|
|
|
// Обработка событий
|
|
|
|
|
$function($request['response']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Новый идентификатор последнего события
|
|
|
|
|
$this->ts = $request['response']['ts'];
|
|
|
|
|
} while (true);
|
|
|
|
|
|
|
|
|
|
return $request;
|
2020-09-08 22:55:21 +07:00
|
|
|
|
}
|
2020-07-09 22:17:26 +07:00
|
|
|
|
}
|