Compare commits

..

No commits in common. "stable" and "1.0.x" have entirely different histories.

5 changed files with 287 additions and 309 deletions

View File

@ -32,7 +32,7 @@ class longpoll
disposition::filename as disposition_filename; disposition::filename as disposition_filename;
} }
const COLLECTION_ACCOUNTS = 'account'; const COLLECTION_USERS = 'user';
const COLLECTION_GROUPS = 'group'; const COLLECTION_GROUPS = 'group';
const COLLECTION_CHATS = 'chat'; const COLLECTION_CHATS = 'chat';
const COLLECTION_MESSAGES = 'message'; const COLLECTION_MESSAGES = 'message';
@ -69,7 +69,6 @@ class longpoll
return true; return true;
} }
} catch (Exception $e) { } catch (Exception $e) {
// Запись ошибки в буфер вывода
terminal::write($e->getMessage() . PHP_EOL . $e->getFile() . ':' . $e->getLine()); terminal::write($e->getMessage() . PHP_EOL . $e->getFile() . ':' . $e->getLine());
} }
@ -79,6 +78,8 @@ class longpoll
/** /**
* Событие: "message_new" * Событие: "message_new"
* *
* Новое сообщение
*
* @param array $data Данные сообщения * @param array $data Данные сообщения
* @param int $group Идентификатор группы * @param int $group Идентификатор группы
* @param string $event Идентификатор события * @param string $event Идентификатор события
@ -90,15 +91,17 @@ class longpoll
if ($this->connection->create) { if ($this->connection->create) {
// Запрошено создание коллекций в случае их отсутствия // Запрошено создание коллекций в случае их отсутствия
// Инициализация коллекций collection::init($this->connection->session, static::COLLECTION_USERS);
collection::init($this->connection->session, static::COLLECTION_ACCOUNTS);
collection::init($this->connection->session, static::COLLECTION_MESSAGES); collection::init($this->connection->session, static::COLLECTION_MESSAGES);
} }
if ($message = document::write($this->connection->session, static::COLLECTION_MESSAGES, static::messages($data, $data['message']['from_id']))) { if ($message = document::write($this->connection->session, static::COLLECTION_MESSAGES, static::messages($data, true))) {
// Записано сообщение // Записано сообщение
// Инициализация субъектов // Инициализация коллекции
$collection = static::COLLECTION_MESSAGES;
// Инициализация аккаунтов
$from = ('mirzaev\\vk\\arangodb\\vk\\' . static::type($data['message']['from_id']))::init($this->connection->session, $data['message']['from_id']); $from = ('mirzaev\\vk\\arangodb\\vk\\' . static::type($data['message']['from_id']))::init($this->connection->session, $data['message']['from_id']);
$to = ('mirzaev\\vk\\arangodb\\vk\\' . static::type($data['message']['peer_id']))::init($this->connection->session, $data['message']['peer_id']); $to = ('mirzaev\\vk\\arangodb\\vk\\' . static::type($data['message']['peer_id']))::init($this->connection->session, $data['message']['peer_id']);
@ -108,15 +111,14 @@ class longpoll
if ($this->connection->create) { if ($this->connection->create) {
// Запрошено создание коллекций в случае их отсутствия // Запрошено создание коллекций в случае их отсутствия
// Инициализация коллекции
collection::init($this->connection->session, static::COLLECTION_ACCESSED, edge: true); collection::init($this->connection->session, static::COLLECTION_ACCESSED, edge: true);
} }
if (document::write($this->connection->session, static::COLLECTION_ACCESSED, ['_from' => $from->getId(), '_to' => $message])) { if (document::write($this->connection->session, static::COLLECTION_ACCESSED, [], ['_from' => $from->getId(), '_to' => $message])) {
// Записано ребро: АККАУНТ (отправитель) -> СООБЩЕНИЕ // Записано ребро: АККАУНТ (отправитель) -> СООБЩЕНИЕ
} }
if (document::write($this->connection->session, static::COLLECTION_ACCESSED, ['_from' => $message, '_to' => $to->getId()])) { if (document::write($this->connection->session, static::COLLECTION_ACCESSED, [], ['_from' => $message, '_to' => $to->getId()])) {
// Записно ребро: СООБЩЕНИЕ -> АККАУНТ (получатель) // Записно ребро: СООБЩЕНИЕ -> АККАУНТ (получатель)
} }
} }
@ -125,16 +127,12 @@ class longpoll
if ($this->journal && journal::init($this->connection->session, $message)->write('create', [ if ($this->journal && journal::init($this->connection->session, $message)->write('create', [
'account' => $from, 'account' => $from,
'changes' => [ 'changes' => [
'new' => collection::search($this->connection->session, sprintf( 'new' => collection::search($this->connection->session, <<<AQL
<<<'AQL' FOR a IN $collection
FOR a IN %s FILTER a._id == $message
FILTER a._id == "%s"
LIMIT 1 LIMIT 1
RETURN a.data RETURN a.data
AQL, AQL),
static::COLLECTION_MESSAGES,
$message
)),
'old' => null 'old' => null
] ]
])) { ])) {
@ -152,8 +150,8 @@ class longpoll
* Обработка сообщений * Обработка сообщений
* *
* @param array $messages Сообщения или сообщение * @param array $messages Сообщения или сообщение
* @param ?int $id Идентификатор аккаунта которому принадлежат вложения * @param bool $download Активация скачивания файлов на сервер
* @param bool $clean Очистить массив от пустых данных? * @param bool $clean Активация очистки массива от пустых данных сообщения
* *
* @return array Обработанные сообщения (зависит от входных данных) * @return array Обработанные сообщения (зависит от входных данных)
* *
@ -165,16 +163,20 @@ class longpoll
* 5. Переделать $message['vk']['metadata']['conversation']['members']['amount_test'] (или удалить) * 5. Переделать $message['vk']['metadata']['conversation']['members']['amount_test'] (или удалить)
* 6. Разобраться с "Мультидиалогом" для старых версий API и существует ли он в новых * 6. Разобраться с "Мультидиалогом" для старых версий API и существует ли он в новых
*/ */
public static function messages(array $messages, ?int $id = null, bool $clean = true): array public static function messages(array $messages, bool $download = false, bool $clean = true): array
{ {
// Универсализация входных данных - передано одно сообщение if (isset($messages['message'])) {
if (isset($messages['message'])) $buffer[] = &$messages; // Передано одно сообщение
// Инициализация
$buffer[] = &$messages;
}
foreach ($buffer ?? $messages as &$message) { foreach ($buffer ?? $messages as &$message) {
// Перебор сообщений // Перебор сообщений
// Инициализация сообщения
$message = [ $message = [
'data' => [
'id' => [ 'id' => [
'global' => $message['message']['id'], 'global' => $message['message']['id'],
'local' => $message['message']['conversation_message_id'] 'local' => $message['message']['conversation_message_id']
@ -182,7 +184,9 @@ class longpoll
'text' => $message['message']['text'] ?? $message['message']['body'], 'text' => $message['message']['text'] ?? $message['message']['body'],
'forward' => static::messages($message['message']['fwd_messages']), 'forward' => static::messages($message['message']['fwd_messages']),
'reply' => static::messages($message['message']['fwd_messages']), 'reply' => static::messages($message['message']['fwd_messages']),
'attachments' => static::attachments($message['message']['attachments'], $id, $clean), 'attachments' => static::attachments($message['message']['attachments'], $download, $clean)
],
'metadata' => [
'date' => [ 'date' => [
'create' => $message['message']['date'] ?? null, 'create' => $message['message']['date'] ?? null,
'update' => $message['message']['update_time'] ?? null 'update' => $message['message']['update_time'] ?? null
@ -242,10 +246,11 @@ class longpoll
], ],
'carousel' => $message['client_info']['carousel'] ?? null, 'carousel' => $message['client_info']['carousel'] ?? null,
'language' => $message['client_info']['lang_id'] ?? null, 'language' => $message['client_info']['lang_id'] ?? null,
]
]; ];
} }
// Очистка массива // Очистка массива, если активировано
$clean and static::cleaner($messages); $clean and static::cleaner($messages);
return $messages; return $messages;
@ -255,12 +260,12 @@ class longpoll
* Обработка вложений * Обработка вложений
* *
* @param array $attachments Вложения * @param array $attachments Вложения
* @param ?int $id Идентификатор аккаунта которому принадлежат изображения * @param bool $download Активация скачивания файлов на сервер
* @param bool $clean Очистить массив от пустых данных? * @param bool $clean Активация очистки массива от пустых данных сообщения
* *
* @return array Обработанные вложения * @return array Обработанные вложения
*/ */
public static function attachments(array $attachments, ?int $id = null, bool $clean = true): array public static function attachments(array $attachments, bool $download = false, bool $clean = false): array
{ {
foreach ($attachments as &$attachment) { foreach ($attachments as &$attachment) {
// Перебор вложений // Перебор вложений
@ -284,7 +289,7 @@ class longpoll
'key' => $attachment['photo']['access_key'] ?? null 'key' => $attachment['photo']['access_key'] ?? null
], ],
'text' => $attachment['photo']['text'] ?? null, 'text' => $attachment['photo']['text'] ?? null,
'storage' => static::sizes($attachment['photo']['sizes'], $id) ?? null 'storage' => static::sizes($attachment['photo']['sizes'], $download) ?? null
], ],
'metadata' => [ 'metadata' => [
'type' => $attachment['type'] ?? null 'type' => $attachment['type'] ?? null
@ -293,35 +298,36 @@ class longpoll
} }
} }
// Очистка массива // Очистка массива, если активировано
$clean and static::cleaner($attachments); $clean and static::cleaner($attachments);
return $attachments; return $attachments;
} }
/** /**
* Сортировка размеров изображения из вложения * Сортировка размеров файла из вложения
* *
* @see https://vk.com/dev/photo_sizes * @param array $sizes Размеры файла согласно спецификации в API
* * @param bool $download Активация скачивания файлов на сервер
* @param array $sizes Размеры изображения согласно спецификации в API * @param int|null $id Идентификатор аккаунта кому принадлежат изображения
* @param ?int $id Идентификатор аккаунта которому принадлежат изображения
* *
* @return array Обработанные размеры * @return array Обработанные размеры
*
* @see https://vk.com/dev/photo_sizes
*/ */
public static function sizes(array $sizes, ?int $id = null): array public static function sizes(array $sizes, bool $download = false, int|null $id = null): array
{ {
foreach ($sizes as &$size) { foreach ($sizes as &$size) {
// Перебор размеров // Перебор размеров
if (isset($id)) { if ($download) {
// Запрошена запись файлов на сервер // Запрошено сохранение на сервер
// Инициализация // Инициализация
$browser = new Guzzle(); $browser = new Guzzle();
// Инициализация директории // Инициализация директории
if (!file_exists($path = static::$path_storage . static::$path_storage_accounts . PHP_EOL . $id . static::$path_storage_accounts_images . PHP_EOL . date('Y_m_d', time()))) if (!file_exists($path = static::$path_storage . (isset($id) ? static::$path_storage_accounts . PHP_EOL . $id : static::$path_storage_vk) . static::$path_storage_accounts_images . PHP_EOL . date('Y_m_d', time())))
if (!mkdir($path, 0755, true)) if (!mkdir($path, 0755, true))
throw new Exception('Не удалось инициализировать директорию: ' . $path); throw new Exception('Не удалось инициализировать директорию: ' . $path);
@ -365,13 +371,8 @@ class longpoll
return $sizes; return $sizes;
} }
/**
* Очистить массив от пустых значений // Инициализация
*
* @param array $target Обрабатываемый массив
*
* @return bool Массив был изменён?
*/
public static function cleaner(array &$target): bool public static function cleaner(array &$target): bool
{ {
// Инициализация // Инициализация
@ -399,16 +400,9 @@ class longpoll
return $changes; return $changes;
} }
/** public function truncate()
* Очистить базу данных
*
* Предполагается использование для автоматизации тестирования
*
* @return void
*/
public function truncate(): void
{ {
collection::truncate($this->connection->session, static::COLLECTION_ACCOUNTS); collection::truncate($this->connection->session, static::COLLECTION_USERS);
collection::truncate($this->connection->session, static::COLLECTION_GROUPS); collection::truncate($this->connection->session, static::COLLECTION_GROUPS);
collection::truncate($this->connection->session, static::COLLECTION_CHATS); collection::truncate($this->connection->session, static::COLLECTION_CHATS);
collection::truncate($this->connection->session, static::COLLECTION_MESSAGES); collection::truncate($this->connection->session, static::COLLECTION_MESSAGES);
@ -417,11 +411,11 @@ class longpoll
} }
/** /**
* Определить тип субъекта * Определить тип аккаунта
* *
* @param int $id Идентификатор * @param int $id Идентификатор
* *
* @return string Возвращает static::COLLECTION_ACCOUNTS, если не прошли другие проверки * @return string Возвращает 'user', если не прошли другие проверки
*/ */
public static function type(int $id): string public static function type(int $id): string
{ {
@ -429,9 +423,8 @@ class longpoll
if ($id - 2000000000 >= 0) return static::COLLECTION_CHATS; if ($id - 2000000000 >= 0) return static::COLLECTION_CHATS;
// Группа // Группа
if ($id < 0) return static::COLLECTION_GROUPS; if (((string) $id)[0] === '-') return static::COLLECTION_GROUPS;
// Аккаунт return static::COLLECTION_USERS;
return static::COLLECTION_ACCOUNTS;
} }
} }

View File

@ -1,90 +0,0 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\vk;
// Модуль ArangoDB для фреймворка ВКонтакте
use mirzaev\vk\arangodb\longpoll,
mirzaev\vk\arangodb\journal;
// Фреймворк ArangoDB
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для работы с API ArabgoDB
use ArangoDBClient\Document as _document,
ArangoDBClient\Connection as _connection;
/**
* Аккаунт ВКонтакте
*
* @package mirzaev\vk\arangodb\vk
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
class account
{
/**
* Инициализировать
*
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал?
* @param bool $create Создавать коллекции при их отсутствии?
*
* @return ?_document Инстанция документа
*/
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{
if ($create) {
// Запрошено создание коллекций в случае их отсутствия
// Инициализация коллекции
collection::init($session, longpoll::COLLECTION_ACCOUNTS);
}
// Поиск
if ($account = static::search($session, $id));
else if ($account = document::write($session, longpoll::COLLECTION_ACCOUNTS, ['id' => $id]) and $journal)
journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, sprintf(
<<<'AQL'
FOR x IN %s
FILTER x._id == "%s"
LIMIT 1
RETURN x
AQL,
longpoll::COLLECTION_ACCOUNTS,
$account
)),
'old' => null
]
]);
// Поиск
return static::search($session, $id);
}
/**
* Найти
*
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
*
* @return ?_document Инстанция документа
*/
public static function search(_connection $session, int $id): ?_document
{
return collection::search($session, sprintf(
<<<'AQL'
FOR x IN %s
FILTER x.id == %u
LIMIT 1
RETURN x
AQL,
longpoll::COLLECTION_ACCOUNTS,
$id
));
}
}

View File

@ -4,24 +4,14 @@ declare(strict_types=1);
namespace mirzaev\vk\arangodb\vk; namespace mirzaev\vk\arangodb\vk;
// Модуль ArangoDB для фреймворка ВКонтакте use mirzaev\vk\arangodb\longpoll;
use mirzaev\vk\arangodb\longpoll, use mirzaev\vk\arangodb\journal;
mirzaev\vk\arangodb\journal; use mirzaev\arangodb\collection;
use mirzaev\arangodb\document;
use ArangoDBClient\Document as _document;
// Фреймворк ArangoDB use ArangoDBClient\Connection as _connection;
use mirzaev\arangodb\collection,
mirzaev\arangodb\document;
// Библиотека для работы с API ArabgoDB
use ArangoDBClient\Document as _document,
ArangoDBClient\Connection as _connection;
/**
* Чат ВКонтакте
*
* @package mirzaev\vk\arangodb\vk
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
*/
class chat class chat
{ {
/** /**
@ -29,62 +19,64 @@ class chat
* *
* @param _connection $session Сессия соединения с базой данных * @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте * @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал? * @param bool $journal Записывать в журнал
* @param bool $create Создавать коллекции при их отсутствии? * @param bool $create Создавать коллекции при их отсутствии
* *
* @return ?_document Инстанция документа * @return ?_document Инстанция документа
*/ */
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{ {
// Инициализация коллекции
$collection = longpoll::COLLECTION_CHATS;
if ($create) { if ($create) {
// Запрошено создание коллекций в случае их отсутствия // Запрошено создание коллекций в случае их отсутствия
// Создание коллекции // Создание коллекции
collection::init($session, longpoll::COLLECTION_CHATS); collection::init($session, $collection);
} }
// Поиск if ($account = collection::search($session, <<<AQL
if ($chat = static::search($session, $id)); FOR x IN $collection
else if ($chat = document::write($session, longpoll::COLLECTION_CHATS, ['id' => $id]) and $journal) FILTER x.data.data.id == $id
journal::init($session, $chat)->write('create', [
'changes' => [
'new' => collection::search($session, sprintf(
<<<'AQL'
FOR x IN %s
FILTER x._id == "%s"
LIMIT 1 LIMIT 1
RETURN x RETURN x.data
AQL, AQL)) {
longpoll::COLLECTION_CHATS, // Найден аккаунт
$chat
)), return $account;
}
if ($account = document::write($session, $collection, ['data' => ['id' => $id]])) {
// Аккаунт записан
// Журналирование
if ($journal && journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, <<<AQL
FOR a IN $collection
FILTER a._id == $account
LIMIT 1
RETURN a.data
AQL),
'old' => null 'old' => null
] ]
]); ])) {
// Записаны данные в журнал
// Поиск
return static::search($session, $id);
} }
/** if ($account = collection::search($session, <<<AQL
* Найти в базе данных FOR x IN $collection
* FILTER x.data.data.id == $id
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
*
* @return ?_document Инстанция документа
*/
public static function search(_connection $session, int $id): ?_document
{
return collection::search($session, sprintf(
<<<'AQL'
FOR x IN %s
FILTER x.id == %u
LIMIT 1 LIMIT 1
RETURN x RETURN x
AQL, AQL)) {
longpoll::COLLECTION_CHATS, // Найден аккаунт
$id
)); return $account;
}
}
return null;
} }
} }

View File

@ -19,63 +19,64 @@ class group
* *
* @param _connection $session Сессия соединения с базой данных * @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте * @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал? * @param bool $journal Записывать в журнал
* @param bool $create Создавать коллекции при их отсутствии? * @param bool $create Создавать коллекции при их отсутствии
* *
* @return ?_document Инстанция документа * @return ?_document Инстанция документа
*/ */
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{ {
// Инициализация коллекции
$collection = longpoll::COLLECTION_GROUPS;
if ($create) { if ($create) {
// Запрошено создание коллекций в случае их отсутствия // Запрошено создание коллекций в случае их отсутствия
// Создание коллекции // Создание коллекции
collection::init($session, longpoll::COLLECTION_GROUPS); collection::init($session, $collection);
} }
// Поиск if ($account = collection::search($session, <<<AQL
if ($group = static::search($session, $id)); FOR x IN $collection
else if ($group = document::write($session, longpoll::COLLECTION_GROUPS, ['id' => $id]) and $journal) FILTER x.data.data.id == $id
journal::init($session, $group)->write('create', [
'changes' => [
'new' => collection::search($session, sprintf(
<<<'AQL'
FOR x IN %s
FILTER x._id == "%s"
LIMIT 1 LIMIT 1
RETURN x RETURN x.data
AQL, AQL)) {
longpoll::COLLECTION_GROUPS, // Найден аккаунт
$group
)), return $account;
}
if ($account = document::write($session, $collection, ['data' => ['id' => $id]])) {
// Аккаунт записан
// Журналирование
if ($journal && journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, <<<AQL
FOR a IN $collection
FILTER a._id == $account
LIMIT 1
RETURN a.data
AQL),
'old' => null 'old' => null
] ]
]); ])) {
// Записаны данные в журнал
// Поиск
return static::search($session, $id);
} }
/** if ($account = collection::search($session, <<<AQL
* Найти в базе данных FOR x IN $collection
* FILTER x.data.data.id == $id
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
*
* @return ?_document Инстанция документа
*/
public static function search(_connection $session, int $id): ?_document
{
return collection::search($session, sprintf(
<<<'AQL'
FOR x IN %s
FILTER x.id == %d
LIMIT 1 LIMIT 1
RETURN x RETURN x
AQL, AQL)) {
longpoll::COLLECTION_GROUPS, // Найден аккаунт
$id
)); return $account;
}
}
return null;
} }
} }

View File

@ -0,0 +1,82 @@
<?php
declare(strict_types=1);
namespace mirzaev\vk\arangodb\vk;
use mirzaev\vk\arangodb\longpoll;
use mirzaev\vk\arangodb\journal;
use mirzaev\arangodb\collection;
use mirzaev\arangodb\document;
use ArangoDBClient\Document as _document;
use ArangoDBClient\Connection as _connection;
class user
{
/**
* Инициализация
*
* @param _connection $session Сессия соединения с базой данных
* @param int $id Идентификатор ВКонтакте
* @param bool $journal Записывать в журнал
* @param bool $create Создавать коллекции при их отсутствии
*
* @return ?_document Инстанция документа
*/
public static function init(_connection $session, int $id, bool $journal = true, bool $create = true): ?_document
{
// Инициализация коллекции
$collection = longpoll::COLLECTION_USERS;
if ($create) {
// Запрошено создание коллекций в случае их отсутствия
// Создание коллекции
collection::init($session, $collection);
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.id == $id
LIMIT 1
RETURN x
AQL)) {
// Найден аккаунт
return $account;
}
if ($account = document::write($session, $collection, ['id' => $id])) {
// Аккаунт записан
// Журналирование
if ($journal && journal::init($session, $account)->write('create', [
'changes' => [
'new' => collection::search($session, <<<AQL
FOR a IN $collection
FILTER a._id == $account
LIMIT 1
RETURN a.data
AQL),
'old' => null
]
])) {
// Записаны данные в журнал
}
if ($account = collection::search($session, <<<AQL
FOR x IN $collection
FILTER x.data.data.id == $id
LIMIT 1
RETURN x
AQL)) {
// Найден аккаунт
return $account;
}
}
return null;
}
}