Добавлен 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
/temp
/vendor
.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;
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;
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)
{
// Инициализация робота
if (!$robot->id) {
throw new Exception('Роботу необходимо задать идентификатор ВКонтакте');
}
if (!$robot->token) {
throw new Exception('Роботу необходимо задать токен для доступа к LongPoll');
}
if (!$robot->version) {
throw new Exception('Роботу необходимо задать версию используемого API ВКонтакте');
}
$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 (!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;
}
list($this->key, $this->server, $this->ts) = [$data['key'], $data['server'], $data['ts']];
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);
}
/**
* Получить события
*
* @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;
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) {
// Если активирована проверка отправленного сообщения
// Запрос: получить сообщение по 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 get()
{
}
public static function delete()
{
return $request;
}
}

View File

@ -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' => 'Метод не поддерживается'];
}
}

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;
/**
* Абстракция браузера
*
* @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 public static function post($method, $params = []);
abstract public static function getToken($url);
abstract protected static function checkSSL($domain);
/**
* Запрос к VK API
*
* @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 Exception;
/**
* Реализация CURL
*
* @method protected static put(string $url, ...$params) Создать
*
* @package Browsers
* @author Arsen Mirzaev
*/
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($url, $params = [])
public static function post(string $url, array $params = null): array
{
$c = curl_init();
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_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_POSTFIELDS, $params);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($c, CURLOPT_SSL_VERIFYPEER, 0);
$result = json_decode(curl_exec($c), True);
curl_close($c);
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
if (isset($result['response'])) {
return $result;
}
public static function getToken($url)
{
if (!self::checkSSL('localhost')) {
$core = Core::init();
$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];
} 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 ['response' => $result];
}
}

View File

@ -4,118 +4,108 @@ declare(strict_types=1);
namespace VK;
use \Exception;
use \VK\Core;
use \VK\BuildAbstract;
use \VK\Robots\RobotAbstract;
use \VK\Robots\Group;
use \VK\Robots\User;
use \VK\Browsers\BrowserAbstract;
use \VK\Proxies\ProxyAbstract;
use \VK\Captchas\CaptchaAbstract;
use \VK\Loggers\Jasmo;
use \VK\Core,
\VK\Robots\RobotAbstract,
\VK\Robots\Group,
\VK\Robots\User,
\VK\Loggers\Jasmo;
/**
* Сборщик
*
* @package Builder
* @property Core $core Ядро
*
* @method public group() Создание робота-группы
* @method public user() Создание робота-пользователя
* @method public function __construct(Core $core = null) Инициализация
* @method public function group() Создание робота-группы
* @method public function user() Создание робота-пользователя
* @method private function reg() Регистрация в ядре
* @method public function log(string $file = null) Журналирование
*
* @method private reg() Регистрация в ядре
*
* @author Arsen Mirzaev
* @package VK\Builder
* @author Arsen Mirzaev <red@hood.su>
*/
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
*/
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
*/
public function user(): User
{
return $this->reg(new \VK\Robots\User());
return $this->reg(new User());
}
/**
* Регистрация в ядре
*
* @param RobotAbstract $robot Регистрируемый робот
*
* @return RobotAbstract
*
* @todo Добавить создание нового процесса (многопоточность)
*/
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 RobotAbstract
* @param string $file Файл для журналирования
*
* @return Builder
*
* @todo Добавить установку иного журналиста по спецификации PSR-3
* @todo Более гибкое журналирование
*/
public function log($file = null): Builder
public function log(string $file = null): Builder
{
// Инициализация журналиста (требует переработки)
Jasmo::init()::post($file)::postErrorHandler()::postShutdownHandler();
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;
use \VK\Loggers\Jasmo;
use \VK\Traits\Singleton;
use \VK\Traits\Registry;
use \VK\Builder,
\VK\Loggers\Jasmo,
\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
*
* @property int robots Количество роботов
*
* @method build(...$params) Инициализация сборщика
*
* @author Arsen Mirzaev
* @author Арсен Мирзаев <red@hood.su>
*/
class Core
{
@ -32,7 +39,7 @@ class Core
*
* @var int
*/
public static int $robots = 0;
public int $robots = 0;
/**
* Временная зона
@ -41,7 +48,7 @@ class Core
*
* @var string
*/
public static string $timezone = 'Europe/Moscow';
public string $timezone = 'Europe/Moscow';
/**
* Пути
@ -50,39 +57,52 @@ class Core
*
* @var array
*/
public static array $path = [
public array $path = [
'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
*/
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;
use VK\Browsers\BrowserAbstract;
use VK\Parsers\Curl;
use Exception;
use VK\API\LongPoll;
use \VK\Browsers\BrowserAbstract,
\VK\API\Methods\Message,
\VK\API\LongPoll;
/**
* Робот: "Группа"
* Робот-группа ВКонтакте
*
* Класс реализовывающий работу от лица группы ВКонтакте
*
* @package Robots
* @author Arsen Mirzaev
* @property LongPoll $longpoll LongPoll-сессия
*
* @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
{
/**
* ВКонтакте: тип 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 postMethod($method, $params = []): BrowserAbstract
public function __set($name, $value): void
{
$browser = __NAMESPACE__ . '\\Browsers\\' . ucfirst($_ENV['BROWSER_TYPE']);
return (new $browser)->post();
parent::__set($name, $value);
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()
{
return new CallBack($this, $params);
if ($name === 'longpoll') {
if (empty($this->longpoll)) {
$this->longpoll = new LongPoll($this);
}
protected function genToken(): string
{
return 'test';
return $this->longpoll;
}
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;
use \VK\Browsers\BrowserAbstract;
use \VK\Proxies\ProxyAbstract;
use \VK\Proxies\CaptchaAbstract;
use \Exception;
use \VK\Browsers\BrowserAbstract,
\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
{
/**
@ -17,6 +40,13 @@ abstract class RobotAbstract
*/
protected int $id;
/**
* Сессия
*
* @var int
*/
protected int $session;
/**
* Токен
*
@ -29,17 +59,17 @@ abstract class RobotAbstract
*
* @var float
*/
public float $version = 5.103;
protected float $version = 5.124;
/**
* Используемый браузер
* Браузер
*
* @var BrowserAbstract
*/
protected BrowserAbstract $browser;
/**
* Прокси-сервер
* Прокси
*
* @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)) {
$this->id = $value;
} else if ($name === 'session' && empty($this->session)) {
$this->session = $value;
} else if ($name === 'token' && empty($this->token)) {
$this->token = $value;
} else if ($name === 'version' && empty($this->version)) {
@ -77,12 +112,16 @@ abstract class RobotAbstract
/**
* Магический геттер
*
* @param mixed $name Название
*
* @return mixed
*/
public function __get($name)
{
if ($name === 'id') {
return $this->id;
} else if ($name === 'session') {
return $this->session;
} else if ($name === 'token') {
return $this->token;
} else if ($name === 'version') {
@ -97,7 +136,9 @@ abstract class RobotAbstract
}
/**
* Установка токена
* Токен
*
* @param string $token Токен
*
* @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;
return $this;
if (isset($this->browser) && $browser === null) {
// Если не передан браузер и свойство иниициализировано
return $this->browser;
} else if ($browser === null) {
// Иначе если не передан браузер свойство не инициализировано
return $this->browser = new \VK\Browsers\Curl();
} else {
// Иначе полученный браузер записывается в свойство и возвращается
return $this->browser = $browser;
}
}
/**
* Установка прокси-сервера
* Установка прокси
*
* @param ProxyAbstract $proxy Прокси
*
* @return RobotAbstract
*/
@ -134,7 +186,9 @@ abstract class RobotAbstract
}
/**
* Установка сервиса антикапчи
* Установка обработчика капчи
*
* @param CaptchaAbstract $captcha Обработчик капчи
*
* @return RobotAbstract
*/
@ -145,38 +199,27 @@ abstract class RobotAbstract
return $this;
}
/**
* Получение браузера
*
* @return int
*/
public function getBrowser(): BrowserAbstract
{
// return $this->browser;
return new \VK\Browsers\Curl;
}
// /**
// * Метод получения токена аккаунта
// *
// * @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 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;
}
// return $this->token_access;
// }
abstract protected function genToken(): string;
abstract protected function genTokenMobile(string $captcha_key, int $captcha_id): string;
// abstract protected function genToken(): string;
// abstract protected function genTokenMobile(string $captcha_key, int $captcha_id): string;
// /**
// * @param null $access_groups

View File

@ -49,7 +49,7 @@ trait Registry
*
* @param mixed $key
*
* @return false|mixed
* @return mixed
*/
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()
{
}
/**
* Инициализатор экземпляра класса
* Инициализация
*
* @return LoggerAbstract
* @return self
*/
public static function init(): self
{
if (self::$instance === null) {
if (!isset(self::$instance)) {
self::$instance = new self;
}
return self::$instance;
}
/**
* Блокировка магического метода __clone()
* Заблокирован
*/
private function __clone()
{
}
/**
* Блокировка магического метода __sleep()
* Заблокирован
*/
private function __sleep()
{
}
/**
* Блокировка магического метода __wakeup()
* Заблокирован
*/
private function __wakeup()
{