Добавлен LongPoll. Переработка ядра, переработка сборщика, переработка сообщений.

This commit is contained in:
RedHood 2020-09-26 20:28:13 +10:00
parent 48946d9070
commit ce92d44b8b
15 changed files with 1497 additions and 1079 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/logs /logs
/temp
/vendor /vendor
.env .env

1446
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,75 +4,268 @@ declare(strict_types=1);
namespace VK\API; 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 * @var string
*/ */
private $auth_type; private RobotAbstract $robot;
/** /**
* Ключ сессии * Ключ к серверу
*
* @see $this->get()
* *
* @var string * @var string
*/ */
private $key; private string $key;
/**
* @var int
*/
private $user_id;
/**
* @var int
*/
private $group_id;
/**
* @var int
*/
private $ts;
/** /**
* Сервер (URL)
*
* @see $this->get()
*
* @var string * @var string
*/ */
private $server; private string $server;
public function __construct(object $robot, array $params = []) /**
* Идентификатор последнего события
*
* От него отсчитываются новые, необработанные события
*
* @see $this->get()
*
* @var string
*/
private string $ts;
/**
* Инициализация
*
* @param object $robot Робот
*/
public function __construct(object $robot)
{ {
$this->robot = $robot; // Инициализация робота
if ($_ENV['ROBOT_TYPE']) { if (!$robot->id) {
$this->robot->auth_type = 'user'; throw new Exception('Роботу необходимо задать идентификатор ВКонтакте');
$this->user_id = $this->robot->request('users.get', [])['id']; }
} else { if (!$robot->token) {
$this->robot->auth_type = 'group'; throw new Exception('Роботу необходимо задать токен для доступа к LongPoll');
$this->group_id = $this->robot->request('groups.getById', [])['id']; }
$this->robot->request('groups.setLongPollSettings', [ if (!$robot->version) {
'group_id' => $this->group_id, throw new Exception('Роботу необходимо задать версию используемого API ВКонтакте');
'enabled' => 1, }
'api_version' => $this->robot->version, $this->robot = $robot;
'message_new' => 1,
]); // Остановка процессов-дубликатов
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]); $settings = [
} else { 'group_id' => $this->robot->id,
$data = $this->robot->request('groups.getLongPollServer', ['group_id' => $this->group_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;
} }
} }

View File

@ -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();
}

View File

@ -4,43 +4,76 @@ declare(strict_types=1);
namespace VK\API\Methods; namespace VK\API\Methods;
use \VK\Core; use \Exception;
use \VK\API\Traits\Request; use \VK\Core,
use \VK\Robots\RobotAbstract; \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 class Message
{ {
use Request; /**
* Отправить сообщение
private const METHOD = 'messages.send'; *
* @param $from Экземпляр робота
public static function post($from, int $to, string $message, int $trolling) * @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)) { $random_id = time();
// Если получен идентификатор, а не экземпляр RobotAbstract $check = false;
// Поиск в регистре // Режим доставки сообщений (усложнение по возрастанию)
$from = Core::init()->get($from); if ($mode >= 1) {
// Дополнительное умножение
$random_id *= rand();
if ($mode >= 2) {
// Проверка отправки (по умолчанию)
$check = true;
}
} }
// Параметры // Запрос: отправить сообщение
$params = [ $request = $from->browser()->api('messages.send', [
'message' => $message,
'peer_id' => $to,
'access_token' => $from->token, 'access_token' => $from->token,
'v' => $from->version, 'v' => $from->version,
'random_id' => $trolling 'peer_id' => $to,
]; 'message' => $message,
'random_id' => $random_id
]);
// Запрос if ($check) {
self::request(self::METHOD, $params, $from->getBrowser()); // Если активирована проверка отправленного сообщения
}
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;
{
} }
} }

View File

@ -4,9 +4,56 @@ declare(strict_types=1);
namespace VK\API\Methods; 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 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' => 'Метод не поддерживается'];
}
} }

View File

@ -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;
}
}

View File

@ -4,9 +4,108 @@ declare(strict_types=1);
namespace VK\Browsers; namespace VK\Browsers;
/**
* Абстракция браузера
*
* @method public static api(string $method, ...$params) Запрос к 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 Browsers
* @author Arsen Mirzaev
*/
abstract class BrowserAbstract abstract class BrowserAbstract
{ {
abstract public static function post($method, $params = []); /**
abstract public static function getToken($url); * Запрос к VK API
abstract protected static function checkSSL($domain); *
* @param string $method Метод VK API
* @param array $params Передаваемые параметры
*
* @return string
*/
public function api(string $method, array $params): array
{
$url = 'https://api.vk.com/method/' . $method;
// while (True) {
// try {
return $this->post($url, $params);
// } 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('Жопа');
// }
// }
}
/**
* Создать
*
* Для запросов на создание (REST)
* Реализация HTTP PUT
*
* @param string $url Запрашиваемая ссылка
* @param array $params Передаваемые параметры
*
* @return array Ответ сервера
*/
public static function put(string $url, array $params = null): array
{
return ['error' => 'Метод не поддерживается'];
}
/**
* Изменить
*
* Для запросов на изменение (REST)
* Реализация HTTP POST
*
* @param string $url Запрашиваемая ссылка
* @param array $params Передаваемые параметры
*
* @return array Ответ сервера
*/
public static function post(string $url, array $params = null): array
{
return ['error' => 'Метод не поддерживается'];
}
/**
* Получить
*
* Для запросов на получение (REST)
* Реализация HTTP GET
*
* @param string $url Запрашиваемая ссылка
* @param array $params Передаваемые параметры
*
* @return array Ответ сервера
*/
public static function get(string $url, array $params = null): array
{
return ['error' => 'Метод не поддерживается'];
}
/**
* Удалить
*
* Для запросов на удаление (REST)
* Реализация HTTP DELETE
*
* @param string $url Запрашиваемая ссылка
* @param array $params Передаваемые параметры
*
* @return array Ответ сервера
*/
public static function delete(string $url, array $params = null): array
{
return ['error' => 'Метод не поддерживается'];
}
} }

View File

@ -6,92 +6,49 @@ namespace VK\Browsers;
use VK\Core; use VK\Core;
use Exception; use Exception;
/**
* Реализация CURL
*
* @method protected static put(string $url, ...$params) Создать
*
* @package Browsers
* @author Arsen Mirzaev
*/
class Curl extends BrowserAbstract class Curl extends BrowserAbstract
{ {
/** /**
* SSL шифрование * Изменить
* *
* @var bool * Для запросов на изменение (REST)
* Реализация HTTP POST
*
* @param string $url Запрашиваемая ссылка
* @param array $params Передаваемые параметры
*
* @return array Ответ сервера
*/ */
private static bool $ssl = false; public static function post(string $url, array $params = null): array
public static function post($url, $params = [])
{ {
$c = curl_init(); $c = curl_init();
curl_setopt($c, CURLOPT_HTTPHEADER, [ curl_setopt($c, CURLOPT_HTTPHEADER, [
"Content-Type:multipart/form-data" 'Content-Type: multipart/form-data'
]); ]);
curl_setopt($c, CURLOPT_URL, $url); curl_setopt($c, CURLOPT_URL, $url);
curl_setopt($c, CURLOPT_POSTFIELDS, $params);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_POSTFIELDS, $params);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0);
$result = json_decode(curl_exec($c), True); $result = json_decode(curl_exec($c), True);
curl_close($c); curl_close($c);
if (isset($result['response'])) if (isset($result['response'])) {
return $result['response'];
else if (isset($result['error']))
throw new Exception(json_encode($result), $result['error']['error_code']);
else if (!isset($result))
self::post($url, $params);
else
return $result; return $result;
} } else if (isset($result['error'])) {
throw new Exception(json_encode($result), $result['error']['error_code']);
public static function getToken($url) } else if (!isset($result)) {
{ self::post($url, $params);
if (!self::checkSSL('localhost')) { } else {
$core = Core::init(); return ['response' => $result];
$core->logger->notice('Соединение не защищено. Необходимо включить SSL шифрование');
}
if ($curl = curl_init()) {
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_USERAGENT, $_ENV['USERAGENT']);
if (isset($post_values)) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_values);
}
if ($cookie and isset(self::$cookie)) {
$send_cookie = [];
foreach (self::$cookie as $cookie_name => $cookie_val) {
$send_cookie[] = "$cookie_name=$cookie_val";
}
curl_setopt($curl, CURLOPT_COOKIE, join('; ', $send_cookie));
}
curl_setopt(
$curl,
CURLOPT_HEADERFUNCTION,
function ($curl, $header) use (&$headers) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) // ignore invalid headers
return $len;
$name = strtolower(trim($header[0]));
if (isset($headers) and !array_key_exists($name, $headers))
$headers[$name] = [trim($header[1])];
else
$headers[$name][] = trim($header[1]);
return $len;
}
);
$out = curl_exec($curl);
curl_close($curl);
//if (isset($headers['set-cookie']))
// $this->parseCookie($headers['set-cookie']);
return ['header' => $headers, 'body' => $out];
} }
} }

View File

@ -4,118 +4,108 @@ declare(strict_types=1);
namespace VK; namespace VK;
use \Exception; use \VK\Core,
use \VK\Core; \VK\Robots\RobotAbstract,
use \VK\BuildAbstract; \VK\Robots\Group,
use \VK\Robots\RobotAbstract; \VK\Robots\User,
use \VK\Robots\Group; \VK\Loggers\Jasmo;
use \VK\Robots\User;
use \VK\Browsers\BrowserAbstract;
use \VK\Proxies\ProxyAbstract;
use \VK\Captchas\CaptchaAbstract;
use \VK\Loggers\Jasmo;
/** /**
* Сборщик * Сборщик
* *
* @package Builder * @property Core $core Ядро
* *
* @method public group() Создание робота-группы * @method public function __construct(Core $core = null) Инициализация
* @method public user() Создание робота-пользователя * @method public function group() Создание робота-группы
* @method public function user() Создание робота-пользователя
* @method private function reg() Регистрация в ядре
* @method public function log(string $file = null) Журналирование
* *
* @method private reg() Регистрация в ядре * @package VK\Builder
* * @author Arsen Mirzaev <red@hood.su>
* @author Arsen Mirzaev
*/ */
class Builder class Builder
{ {
public function __construct() /**
* Инициализация
*
* @param Core $core Ядро
*/
public function __construct(Core $core = null)
{ {
return $this; if (isset($core)) {
$this->core = $core::init();
}
} }
/** /**
* Создание робота-группы * Создание робота-группы
* *
* @param int $id Идентификатор
*
* @return Group * @return Group
*/ */
public function group(): Group public function group(int $id): Group
{ {
return $this->reg(new \VK\Robots\Group()); // Создание робота
$robot = new Group();
// Идентификатор
$robot->id = $id;
// Сессия
$robot->session = isset($this->core) ? count($this->core->get($id)) : 0;
return isset($this->core) ? $this->reg($robot) : $robot;
} }
/** /**
* Создание робота-пользователя * Создание робота-пользователя
* *
* @param int $id Идентификатор
*
* @return User * @return User
*/ */
public function user(): User public function user(): User
{ {
return $this->reg(new \VK\Robots\User()); return $this->reg(new User());
} }
/** /**
* Регистрация в ядре * Регистрация в ядре
* *
* @param RobotAbstract $robot Регистрируемый робот
*
* @return RobotAbstract * @return RobotAbstract
* *
* @todo Добавить создание нового процесса (многопоточность) * @todo Добавить создание нового процесса (многопоточность)
*/ */
private function reg(RobotAbstract $robot): RobotAbstract private function reg(RobotAbstract $robot): RobotAbstract
{ {
// Присвоение идентификатора
$robot->id = ++Core::$robots;
// Регистрация в ядре // Регистрация в ядре
Core::set($robot->id, $robot); $this->core->set((int) $robot->id, $robot);
// Количество роботов
++$this->core->robots;
return $robot; return $robot;
} }
/** /**
* Установка журналирования * Журналирование
* *
* @return RobotAbstract * @param string $file Файл для журналирования
*
* @return Builder
* *
* @todo Добавить установку иного журналиста по спецификации PSR-3 * @todo Добавить установку иного журналиста по спецификации PSR-3
* @todo Более гибкое журналирование
*/ */
public function log($file = null): Builder public function log(string $file = null): Builder
{ {
// Инициализация журналиста (требует переработки)
Jasmo::init()::post($file)::postErrorHandler()::postShutdownHandler(); Jasmo::init()::post($file)::postErrorHandler()::postShutdownHandler();
return $this; return $this;
} }
/**
* Установка браузера
*
* @return RobotAbstract
*/
public function browser(BrowserAbstract $browser): Builder
{
$this->browser = $browser;
return $this;
}
/**
* Установка прокси
*
* @return RobotAbstract
*/
public function proxy(ProxyAbstract $proxy): Builder
{
$this->proxy = $proxy;
return $this;
}
/**
* Установка обработчика капч
*
* @return RobotAbstract
*/
public function captcha(CaptchaAbstract $captcha): Builder
{
$this->captcha = $captcha;
return $this;
}
} }

View File

@ -4,20 +4,27 @@ declare(strict_types=1);
namespace VK; namespace VK;
use \VK\Loggers\Jasmo; use \VK\Builder,
use \VK\Traits\Singleton; \VK\Loggers\Jasmo,
use \VK\Traits\Registry; \VK\Traits\Singleton,
\VK\Traits\Registry,
\VK\Robots\RobotAbstract;
/** /**
* Ядро фреймворка для работы с VK API * Ядро
*
* @property int $robots Количество роботов
* @property string $timezone Временная зона (журналирование)
* @property array $path Пути (архитектура проекта)
*
* @method protected static function __construct() Инициализация
* @method public static function init() Запуск инициализации или получение инстанции
* @method public public function build() Сборщик
* @method public function set($id, $value) Запись в реестр
* @method public function get($id = null) Чтение из реестра
* *
* @package VK * @package VK
* * @author Арсен Мирзаев <red@hood.su>
* @property int robots Количество роботов
*
* @method build(...$params) Инициализация сборщика
*
* @author Arsen Mirzaev
*/ */
class Core class Core
{ {
@ -32,7 +39,7 @@ class Core
* *
* @var int * @var int
*/ */
public static int $robots = 0; public int $robots = 0;
/** /**
* Временная зона * Временная зона
@ -41,7 +48,7 @@ class Core
* *
* @var string * @var string
*/ */
public static string $timezone = 'Europe/Moscow'; public string $timezone = 'Europe/Moscow';
/** /**
* Пути * Пути
@ -50,39 +57,52 @@ class Core
* *
* @var array * @var array
*/ */
public static array $path = [ public array $path = [
'root' => '', 'root' => '',
'log' => '' 'logs' => './logs',
'temp' => './temp'
]; ];
protected function __construct() { /**
self::$path = [ * Инициализация
'root' => dirname(__DIR__) . '..', *
'log' => self::$path['root'] . '/log' * @see Singleton->__construct() Внимание на protected
]; */
protected function __construct()
{
$this->path['root'] = $root = dirname(__DIR__);
$this->path['logs'] = $root . '/logs';
$this->path['temp'] = $root . '/temp';
} }
/** /**
* Инициализация сборщика * Сборщик
* *
* @return Builder * @return Builder
*/ */
public function build(): Builder public function build(): Builder
{ {
return new Builder(); return new Builder($this);
} }
/** /**
* Активация журналирования * Запись в реестр
* *
* @return Core * Модификация наследуемого метода
* *
* @todo Добавить установку иного журналиста по спецификации PSR-3 * @param mixed $id
* @param mixed $robot
*
* @return void
*/ */
public function log($file = null): Core public static function set(int $id, RobotAbstract $robot): bool
{ {
Jasmo::init()::post($file)::postErrorHandler()::postShutdownHandler(); if (empty(self::$registry[$id])) {
self::$registry[$id] = [];
}
return $this; array_push(self::$registry[$id], $robot);
return true;
} }
} }

View File

@ -4,64 +4,92 @@ declare(strict_types=1);
namespace VK\Robots; namespace VK\Robots;
use VK\Browsers\BrowserAbstract; use \VK\Browsers\BrowserAbstract,
use VK\Parsers\Curl; \VK\API\Methods\Message,
use Exception; \VK\API\LongPoll;
use VK\API\LongPoll;
/** /**
* Робот: "Группа" * Робот-группа ВКонтакте
* *
* Класс реализовывающий работу от лица группы ВКонтакте * Класс реализовывающий работу от лица группы ВКонтакте
* *
* @package Robots * @property LongPoll $longpoll LongPoll-сессия
* @author Arsen Mirzaev *
* @method public function message(int $to, string $message, int $mode = 2) Отправить сообщение
* @method public function __set($name, $value) Магический метод-сеттер
* @method public function __get($name) Магический метод-геттер
*
* @package VK\Robots\Group
* @author Arsen Mirzaev <red@hood.su>
*
* @todo Вместо методов реализовать фабрику методов API, как было раньше
*/ */
class Group extends RobotAbstract class Group extends RobotAbstract
{ {
/** /**
* ВКонтакте: тип API * LongPoll-сессия
* *
* @var string * @var LongPoll
*/ */
public string $api; protected LongPoll $longpoll;
/** /**
* ВКонтакте: активатор мобильной версии * Отправить сообщение
* *
* @var bool * @param int $to Идентификатор получателя
* @param string $message Текст сообщения
* @param int $mode Режим доставки сообщения
*
* @return array
*/ */
//protected bool $mobile = false; public function message(int $to, string $message, int $mode = 2): array
{
return Message::put($this, $to, $message, $mode);
}
/** /**
* ВКонтакте: идентификатор капчи * Магический метод-сеттер
* *
* @var int * @param mixed $name Название
* @param mixed $value Значение
*
* @see RobotAbstract Модификация наследуемого метода
*
* @return void
*/ */
//protected int $captcha; public function __set($name, $value): void
public function postMethod($method, $params = []): BrowserAbstract
{ {
$browser = __NAMESPACE__ . '\\Browsers\\' . ucfirst($_ENV['BROWSER_TYPE']); parent::__set($name, $value);
return (new $browser)->post();
if ($name === 'longpoll') {
if ($value instanceof LongPoll) {
$this->longpoll = $value;
} else {
$this->longpoll = new LongPoll($this);
}
}
} }
public function longpoll(...$params): LongPoll /**
* Магический метод-геттер
*
* @param mixed $name Название
*
* @see RobotAbstract Модификация наследуемого метода
*
* @return mixed
*/
public function __get($name)
{ {
return new LongPoll($this, $params); if (!empty($parent = parent::__get($name))) {
} return $parent;
}
public function callback() if ($name === 'longpoll') {
{ if (empty($this->longpoll)) {
return new CallBack($this, $params); $this->longpoll = new LongPoll($this);
} }
return $this->longpoll;
protected function genToken(): string }
{
return 'test';
}
protected function genTokenMobile(string $captcha_key, int $captcha_id): string
{
return 'test 2';
} }
} }

View File

@ -4,10 +4,33 @@ declare(strict_types=1);
namespace VK\Robots; namespace VK\Robots;
use \VK\Browsers\BrowserAbstract; use \Exception;
use \VK\Proxies\ProxyAbstract; use \VK\Browsers\BrowserAbstract,
use \VK\Proxies\CaptchaAbstract; \VK\Proxies\ProxyAbstract,
\VK\Captcha\CaptchaAbstract,
\VK\Core;
/**
* Абстракция робота
*
* @property int $id Идентификатор
* @property int $session Сессия
* @property string $token Токен
* @property float $version Версия API
* @property BrowserAbstract $browser Браузер
* @property ProxyAbstract $proxy Прокси
* @property CaptchaAbstract $captcha Обработчик капчи
*
* @method protected static function put(string $url, ...$params) Создать
* @method protected static function post(string $url, ...$params) Изменить
* @method protected static function get(string $url, ...$params) Получить
* @method protected static function delete(string $url, ...$params) Удалить
* @method public function __set($name, $value) Магический метод-сеттер
* @method public function __get($name) Магический метод-геттер
*
* @package API/Methods
* @author Арсен Мирзаев <red@hood.su>
*/
abstract class RobotAbstract abstract class RobotAbstract
{ {
/** /**
@ -17,6 +40,13 @@ abstract class RobotAbstract
*/ */
protected int $id; protected int $id;
/**
* Сессия
*
* @var int
*/
protected int $session;
/** /**
* Токен * Токен
* *
@ -29,17 +59,17 @@ abstract class RobotAbstract
* *
* @var float * @var float
*/ */
public float $version = 5.103; protected float $version = 5.124;
/** /**
* Используемый браузер * Браузер
* *
* @var BrowserAbstract * @var BrowserAbstract
*/ */
protected BrowserAbstract $browser; protected BrowserAbstract $browser;
/** /**
* Прокси-сервер * Прокси
* *
* @var ProxyAbstract * @var ProxyAbstract
*/ */
@ -55,12 +85,17 @@ abstract class RobotAbstract
/** /**
* Магический сеттер * Магический сеттер
* *
* @return mixed * @param mixed $name Название
* @param mixed $value Значение
*
* @return void
*/ */
public function __set($name, $value) public function __set($name, $value): void
{ {
if ($name === 'id' && empty($this->id)) { if ($name === 'id' && empty($this->id)) {
$this->id = $value; $this->id = $value;
} else if ($name === 'session' && empty($this->session)) {
$this->session = $value;
} else if ($name === 'token' && empty($this->token)) { } else if ($name === 'token' && empty($this->token)) {
$this->token = $value; $this->token = $value;
} else if ($name === 'version' && empty($this->version)) { } else if ($name === 'version' && empty($this->version)) {
@ -77,12 +112,16 @@ abstract class RobotAbstract
/** /**
* Магический геттер * Магический геттер
* *
* @param mixed $name Название
*
* @return mixed * @return mixed
*/ */
public function __get($name) public function __get($name)
{ {
if ($name === 'id') { if ($name === 'id') {
return $this->id; return $this->id;
} else if ($name === 'session') {
return $this->session;
} else if ($name === 'token') { } else if ($name === 'token') {
return $this->token; return $this->token;
} else if ($name === 'version') { } else if ($name === 'version') {
@ -97,7 +136,9 @@ abstract class RobotAbstract
} }
/** /**
* Установка токена * Токен
*
* @param string $token Токен
* *
* @return RobotAbstract * @return RobotAbstract
*/ */
@ -110,19 +151,30 @@ abstract class RobotAbstract
/** /**
* Установка браузера * Браузер
* *
* @return RobotAbstract * @param BrowserAbstract $browser Браузер
*
* @return BrowserAbstract
*/ */
public function browser(BrowserAbstract $browser): RobotAbstract public function browser(BrowserAbstract $browser = null): BrowserAbstract
{ {
$this->browser = $browser; if (isset($this->browser) && $browser === null) {
// Если не передан браузер и свойство иниициализировано
return $this; return $this->browser;
} else if ($browser === null) {
// Иначе если не передан браузер свойство не инициализировано
return $this->browser = new \VK\Browsers\Curl();
} else {
// Иначе полученный браузер записывается в свойство и возвращается
return $this->browser = $browser;
}
} }
/** /**
* Установка прокси-сервера * Установка прокси
*
* @param ProxyAbstract $proxy Прокси
* *
* @return RobotAbstract * @return RobotAbstract
*/ */
@ -134,7 +186,9 @@ abstract class RobotAbstract
} }
/** /**
* Установка сервиса антикапчи * Установка обработчика капчи
*
* @param CaptchaAbstract $captcha Обработчик капчи
* *
* @return RobotAbstract * @return RobotAbstract
*/ */
@ -145,38 +199,27 @@ abstract class RobotAbstract
return $this; return $this;
} }
/** // /**
* Получение браузера // * Метод получения токена аккаунта
* // *
* @return int // * @return string
*/ // */
public function getBrowser(): BrowserAbstract // public function getToken($captcha_key = null, $captcha_id = null): string
{ // {
// return $this->browser; // if (!empty($_ENV['ACCOUNT_TOKEN'])) {
return new \VK\Browsers\Curl; // return $_ENV['ACCOUNT_TOKEN'];
} // } else if ($this->mobile) {
// $this->token_access = $this->genTokenMobile($captcha_key, $this->captcha_id ?? $captcha_id);
// } else {
// $this->token_access = $this->genToken();
// }
/** // return $this->token_access;
* Метод получения токена аккаунта // }
*
* @return string
*/
public function getToken($captcha_key = null, $captcha_id = null): string
{
if (!empty($_ENV['ACCOUNT_TOKEN'])) {
return $_ENV['ACCOUNT_TOKEN'];
} else if ($this->mobile) {
$this->token_access = $this->genTokenMobile($captcha_key, $this->captcha_id ?? $captcha_id);
} else {
$this->token_access = $this->genToken();
}
return $this->token_access;
}
abstract protected function genToken(): string; // abstract protected function genToken(): string;
abstract protected function genTokenMobile(string $captcha_key, int $captcha_id): string; // abstract protected function genTokenMobile(string $captcha_key, int $captcha_id): string;
// /** // /**
// * @param null $access_groups // * @param null $access_groups

View File

@ -49,7 +49,7 @@ trait Registry
* *
* @param mixed $key * @param mixed $key
* *
* @return false|mixed * @return mixed
*/ */
public static function get(int $key = null) public static function get(int $key = null)
{ {

View File

@ -12,46 +12,46 @@ trait Singleton
/** /**
* Экземпляр класса * Экземпляр класса
* *
* @var LoggerAbstract * @var object
*/ */
protected static $instance; protected static object $instance;
/** /**
* Блокировка конструктора * Заблокирован
*/ */
protected function __construct() protected function __construct()
{ {
} }
/** /**
* Инициализатор экземпляра класса * Инициализация
* *
* @return LoggerAbstract * @return self
*/ */
public static function init(): self public static function init(): self
{ {
if (self::$instance === null) { if (!isset(self::$instance)) {
self::$instance = new self; self::$instance = new self;
} }
return self::$instance; return self::$instance;
} }
/** /**
* Блокировка магического метода __clone() * Заблокирован
*/ */
private function __clone() private function __clone()
{ {
} }
/** /**
* Блокировка магического метода __sleep() * Заблокирован
*/ */
private function __sleep() private function __sleep()
{ {
} }
/** /**
* Блокировка магического метода __wakeup() * Заблокирован
*/ */
private function __wakeup() private function __wakeup()
{ {