Добавлен LongPoll. Переработка ядра, переработка сборщика, переработка сообщений.
This commit is contained in:
@@ -4,75 +4,268 @@ declare(strict_types=1);
|
||||
|
||||
namespace VK\API;
|
||||
|
||||
class LongPoll extends LongPollAbstract
|
||||
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
|
||||
{
|
||||
/**
|
||||
* Объект взаимодействия лонгпола
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $robot;
|
||||
|
||||
/**
|
||||
* Тип объекта: пользователь или группа
|
||||
* Робот
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $auth_type;
|
||||
private RobotAbstract $robot;
|
||||
|
||||
/**
|
||||
* Ключ сессии
|
||||
* Ключ к серверу
|
||||
*
|
||||
* @see $this->get()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $key;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $user_id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $group_id;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $ts;
|
||||
private string $key;
|
||||
|
||||
/**
|
||||
* Сервер (URL)
|
||||
*
|
||||
* @see $this->get()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $server;
|
||||
|
||||
public function __construct(object $robot, array $params = [])
|
||||
private string $server;
|
||||
|
||||
/**
|
||||
* Идентификатор последнего события
|
||||
*
|
||||
* От него отсчитываются новые, необработанные события
|
||||
*
|
||||
* @see $this->get()
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $ts;
|
||||
|
||||
/**
|
||||
* Инициализация
|
||||
*
|
||||
* @param object $robot Робот
|
||||
*/
|
||||
public function __construct(object $robot)
|
||||
{
|
||||
$this->robot = $robot;
|
||||
if ($_ENV['ROBOT_TYPE']) {
|
||||
$this->robot->auth_type = 'user';
|
||||
$this->user_id = $this->robot->request('users.get', [])['id'];
|
||||
} else {
|
||||
$this->robot->auth_type = 'group';
|
||||
$this->group_id = $this->robot->request('groups.getById', [])['id'];
|
||||
$this->robot->request('groups.setLongPollSettings', [
|
||||
'group_id' => $this->group_id,
|
||||
'enabled' => 1,
|
||||
'api_version' => $this->robot->version,
|
||||
'message_new' => 1,
|
||||
]);
|
||||
// Инициализация робота
|
||||
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);
|
||||
}
|
||||
$this->getLongPollServer();
|
||||
}
|
||||
|
||||
public function getLongPollServer()
|
||||
|
||||
/**
|
||||
* Установить настройки
|
||||
*
|
||||
* Полная настройка и активация LongPoll
|
||||
*
|
||||
* @param bool $status = true Активация или деактивация
|
||||
* @param string ...$params Изменяемые параметры
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function post(bool $status = true, string ...$params): array
|
||||
{
|
||||
if ($this->robot->auth_type == 'user') {
|
||||
$data = $this->robot->request('messages.getLongPollServer', ['need_pts' => 1, 'lp_version' => 3]);
|
||||
} else {
|
||||
$data = $this->robot->request('groups.getLongPollServer', ['group_id' => $this->group_id]);
|
||||
// Инициализация настроек
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
||||
list($this->key, $this->server, $this->ts) = [$data['key'], $data['server'], $data['ts']];
|
||||
|
||||
return $this->robot->browser()->api('groups.setLongPollSettings', $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить события
|
||||
*
|
||||
* @param int $wait Время ожидания новых событий (в секундах)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get(int $wait = 25): array
|
||||
{
|
||||
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'];
|
||||
}
|
||||
|
||||
// Запрос на получение событий
|
||||
return $this->robot->browser()->post($this->server . '?act=a_check&key=' . $this->key . '&ts=' . $this->ts . '&wait=' . $wait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Обработать события
|
||||
*
|
||||
* Получает и обрабатывает события
|
||||
*
|
||||
* @param callable $function Обработка
|
||||
* @param int $wait Время ожидания новых событий (в секундах)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function handle(callable $function, int $wait = 25): array
|
||||
{
|
||||
// Файл-блокировщик и 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;
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace VK\API;
|
||||
|
||||
abstract class LongPollAbstract
|
||||
{
|
||||
abstract public function __construct(object $robot, array $params = []);
|
||||
abstract public function getRobotInfo();
|
||||
}
|
@@ -4,43 +4,76 @@ declare(strict_types=1);
|
||||
|
||||
namespace VK\API\Methods;
|
||||
|
||||
use \VK\Core;
|
||||
use \VK\API\Traits\Request;
|
||||
use \VK\Robots\RobotAbstract;
|
||||
use \Exception;
|
||||
use \VK\Core,
|
||||
\VK\Robots\RobotAbstract;
|
||||
|
||||
/**
|
||||
* Сообщение
|
||||
*
|
||||
* Отправляет сообщение
|
||||
*
|
||||
* @method public static function put(RobotAbstract $from, int $to, string $message, int $mode = 2) Отправить сообщение
|
||||
*
|
||||
* @see https://vk.com/dev/messages.send
|
||||
* @see https://vk.com/dev/messages.getById
|
||||
*
|
||||
* @package VK\API\Methods\Message
|
||||
* @author Арсен Мирзаев <red@hood.su>
|
||||
*
|
||||
* @todo Добавить обработку ошибок ($request['errors];)
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
use Request;
|
||||
|
||||
private const METHOD = 'messages.send';
|
||||
|
||||
public static function post($from, int $to, string $message, int $trolling)
|
||||
/**
|
||||
* Отправить сообщение
|
||||
*
|
||||
* @param $from Экземпляр робота
|
||||
* @param int $to Идентификатор получателя
|
||||
* @param string $message Текст сообщения
|
||||
* @param int $mode Режим доставки сообщения
|
||||
*
|
||||
* @return array Ответ сервера
|
||||
*/
|
||||
public static function put(RobotAbstract $from, int $to, string $message, int $mode = 2): array
|
||||
{
|
||||
if (is_int($from)) {
|
||||
// Если получен идентификатор, а не экземпляр RobotAbstract
|
||||
$random_id = time();
|
||||
$check = false;
|
||||
|
||||
// Поиск в регистре
|
||||
$from = Core::init()->get($from);
|
||||
// Режим доставки сообщений (усложнение по возрастанию)
|
||||
if ($mode >= 1) {
|
||||
// Дополнительное умножение
|
||||
$random_id *= rand();
|
||||
|
||||
if ($mode >= 2) {
|
||||
// Проверка отправки (по умолчанию)
|
||||
$check = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Параметры
|
||||
$params = [
|
||||
'message' => $message,
|
||||
'peer_id' => $to,
|
||||
// Запрос: отправить сообщение
|
||||
$request = $from->browser()->api('messages.send', [
|
||||
'access_token' => $from->token,
|
||||
'v' => $from->version,
|
||||
'random_id' => $trolling
|
||||
];
|
||||
'peer_id' => $to,
|
||||
'message' => $message,
|
||||
'random_id' => $random_id
|
||||
]);
|
||||
|
||||
// Запрос
|
||||
self::request(self::METHOD, $params, $from->getBrowser());
|
||||
}
|
||||
if ($check) {
|
||||
// Если активирована проверка отправленного сообщения
|
||||
|
||||
public static function get()
|
||||
{
|
||||
}
|
||||
// Запрос: получить сообщение по ID
|
||||
if ($from->browser()->api('messages.getById', [
|
||||
'access_token' => $from->token,
|
||||
'v' => $from->version,
|
||||
'message_ids' => is_int($request["response"]) ? $request["response"] : $request["response"]['message_id']
|
||||
])['response']['count'] === 0) {
|
||||
// Если сообщения не существует, то повтор
|
||||
self::put($from, $to, $message, $mode);
|
||||
}
|
||||
}
|
||||
|
||||
public static function delete()
|
||||
{
|
||||
return $request;
|
||||
}
|
||||
}
|
||||
|
@@ -4,9 +4,56 @@ declare(strict_types=1);
|
||||
|
||||
namespace VK\API\Methods;
|
||||
|
||||
/**
|
||||
* Абстракция метода API
|
||||
*
|
||||
* @method protected static put(string $url, ...$params) Создать
|
||||
* @method protected static post(string $url, ...$params) Изменить
|
||||
* @method protected static get(string $url, ...$params) Получить
|
||||
* @method protected static delete(string $url, ...$params) Удалить
|
||||
*
|
||||
* @package VK\API\Methods
|
||||
* @author Арсен Мирзаев <red@hood.su>
|
||||
*/
|
||||
abstract class MethodAbstract
|
||||
{
|
||||
abstract public static function post();
|
||||
abstract public static function get();
|
||||
abstract public static function delete();
|
||||
/**
|
||||
* Создать
|
||||
*
|
||||
* @return array Ответ сервера
|
||||
*/
|
||||
public static function put(): array
|
||||
{
|
||||
return ['error' => 'Метод не поддерживается'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Изменить
|
||||
*
|
||||
* @return array Ответ сервера
|
||||
*/
|
||||
public static function post(): array
|
||||
{
|
||||
return ['error' => 'Метод не поддерживается'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить
|
||||
*
|
||||
* @return array Ответ сервера
|
||||
*/
|
||||
public static function get(): array
|
||||
{
|
||||
return ['error' => 'Метод не поддерживается'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить
|
||||
*
|
||||
* @return array Ответ сервера
|
||||
*/
|
||||
public static function delete(): array
|
||||
{
|
||||
return ['error' => 'Метод не поддерживается'];
|
||||
}
|
||||
}
|
||||
|
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace VK\API\Traits;
|
||||
|
||||
use VK\Browsers\BrowserAbstract;
|
||||
use Exception;
|
||||
/**
|
||||
* Паттерн registry
|
||||
*/
|
||||
trait Request
|
||||
{
|
||||
private static function request(string $method, array $params, BrowserAbstract $browser)
|
||||
{
|
||||
$url = 'https://api.vk.com/method/' . $method;
|
||||
|
||||
foreach ($params as $key => $value) {
|
||||
$post[$key] = $value;
|
||||
}
|
||||
|
||||
// while (True) {
|
||||
// try {
|
||||
return $browser::post($url, $post);
|
||||
// } catch (Exception $e) {
|
||||
// // if (in_array($e->getCode(), $this->request_ignore_error)) {
|
||||
// // sleep(1);
|
||||
// // continue;
|
||||
// // } else
|
||||
// // throw new Exception($e->getMessage(), $e->getCode());
|
||||
// throw new Exception('Жопа');
|
||||
// }
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user