первая инициализация и перенос на сервер
This commit is contained in:
parent
c41a723af7
commit
c3dec4417d
|
@ -1,16 +1,23 @@
|
||||||
{
|
{
|
||||||
"name": "mirzaev/spetsresurs-telegram-registry-requests",
|
"name": "mirzaev/arming_bot",
|
||||||
"type": "robot",
|
"type": "robot",
|
||||||
|
"tags": [
|
||||||
|
"telegram",
|
||||||
|
"chat-robot",
|
||||||
|
"military",
|
||||||
|
"shop"
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"triagens/arangodb": "^3.8",
|
"triagens/arangodb": "^3.8",
|
||||||
"mirzaev/arangodb": "^1.0",
|
"mirzaev/arangodb": "^1.0",
|
||||||
"badfarm/zanzara": "^0.9.1",
|
"badfarm/zanzara": "^0.9.1",
|
||||||
"nyholm/psr7": "^1.8"
|
"nyholm/psr7": "^1.8",
|
||||||
|
"react/filesystem": "^0.1.2"
|
||||||
},
|
},
|
||||||
"license": "WTFPL",
|
"license": "WTFPL",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"mirzaev\\spetsresurs\\telegram\\registry\\requests\\": "mirzaev/spetsresurs/telegram/registry/requests/system/"
|
"mirzaev\\arming_bot\\": "mirzaev/arming_bot/system/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"authors": [
|
"authors": [
|
||||||
|
@ -22,7 +29,8 @@
|
||||||
"minimum-stability": "stable",
|
"minimum-stability": "stable",
|
||||||
"config": {
|
"config": {
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"php-http/discovery": true
|
"php-http/discovery": true,
|
||||||
|
"wyrihaximus/composer-update-bin-autoload-path": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,615 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
// Фреймворк ArangoDB
|
||||||
|
use mirzaev\arangodb\connection,
|
||||||
|
mirzaev\arangodb\collection,
|
||||||
|
mirzaev\arangodb\document;
|
||||||
|
|
||||||
|
// Библиотека для ArangoDB
|
||||||
|
use ArangoDBClient\Document as _document,
|
||||||
|
ArangoDBClient\Cursor,
|
||||||
|
ArangoDBClient\Statement as _statement;
|
||||||
|
|
||||||
|
// Фреймворк Telegram
|
||||||
|
use Zanzara\Zanzara,
|
||||||
|
Zanzara\Context,
|
||||||
|
Zanzara\Config,
|
||||||
|
Zanzara\Telegram\Type\Input\InputFile,
|
||||||
|
Zanzara\Telegram\Type\File\Document as telegram_document,
|
||||||
|
Zanzara\Telegram\Type\File\File,
|
||||||
|
Zanzara\Middleware\MiddlewareNode as Node;
|
||||||
|
|
||||||
|
ini_set('error_reporting', E_ALL);
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
|
||||||
|
// Версия робота
|
||||||
|
define('VERSION', '1.0.0');
|
||||||
|
|
||||||
|
// Путь до настроек
|
||||||
|
define('SETTINGS', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'settings');
|
||||||
|
|
||||||
|
// Путь до хранилища
|
||||||
|
define('STORAGE', __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'storage');
|
||||||
|
|
||||||
|
// Файл в формате xlsx с примером excel-документа для импорта каталога
|
||||||
|
define('CATALOG_EXAMPLE', STORAGE . DIRECTORY_SEPARATOR . 'example.xlsx');
|
||||||
|
|
||||||
|
// Файл в формате xlsx для импорта каталога
|
||||||
|
define('CATALOG_IMPORT', STORAGE . DIRECTORY_SEPARATOR . 'import.xlsx');
|
||||||
|
|
||||||
|
// Ключ чат-робота Telegram
|
||||||
|
define('KEY', require(SETTINGS . DIRECTORY_SEPARATOR . 'key.php'));
|
||||||
|
|
||||||
|
// Инициализация библиотек
|
||||||
|
require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||||
|
|
||||||
|
// Инициализация инстанции соединения с ArangoDB
|
||||||
|
$arangodb = new connection(require SETTINGS . DIRECTORY_SEPARATOR . 'arangodb.php');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Экранирование символов для Markdown
|
||||||
|
*
|
||||||
|
* @param string $text Текст для экранирования
|
||||||
|
* @param array $exception Символы которые будут исключены из списка для экранирования
|
||||||
|
*
|
||||||
|
* @return string Экранированный текст
|
||||||
|
*/
|
||||||
|
function unmarkdown(string $text, array $exceptions = []): string
|
||||||
|
{
|
||||||
|
// Инициализация реестра символом для конвертации
|
||||||
|
$from = array_diff(
|
||||||
|
[
|
||||||
|
'#',
|
||||||
|
'*',
|
||||||
|
'_',
|
||||||
|
'=',
|
||||||
|
'.',
|
||||||
|
'[',
|
||||||
|
']',
|
||||||
|
'(',
|
||||||
|
')',
|
||||||
|
'-',
|
||||||
|
'>',
|
||||||
|
'<',
|
||||||
|
'!',
|
||||||
|
'`'
|
||||||
|
],
|
||||||
|
$exceptions
|
||||||
|
);
|
||||||
|
|
||||||
|
// Инициализация реестра целей для конвертации
|
||||||
|
$to = [];
|
||||||
|
foreach ($from as $symbol) $to[] = "\\$symbol";
|
||||||
|
|
||||||
|
// Конвертация и выход (успех)
|
||||||
|
return str_replace($from, $to, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация запчасти
|
||||||
|
*
|
||||||
|
* Проверяет существование запчасти
|
||||||
|
*
|
||||||
|
* @param string $spare Запчасть
|
||||||
|
*
|
||||||
|
* @return string|bool Запчасть, если найдена, иначе false
|
||||||
|
*/
|
||||||
|
function spares(string $spare): string|bool
|
||||||
|
{
|
||||||
|
// Поиск запчастей и выход (успех)
|
||||||
|
return match (mb_strtolower($spare)) {
|
||||||
|
'цевьё' => 'Цевьё',
|
||||||
|
default => false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Авторизация
|
||||||
|
*
|
||||||
|
* @param string $id Идентификатор Telegram
|
||||||
|
* @param bool $registration Регистрация, если аккаунт не найден
|
||||||
|
*
|
||||||
|
* @return _document|null Инстанция аккаунта, если найден
|
||||||
|
*/
|
||||||
|
function authorization(string $id, bool $registration = true): _document|null
|
||||||
|
{
|
||||||
|
global $arangodb;
|
||||||
|
|
||||||
|
if (collection::init($arangodb->session, 'account')) {
|
||||||
|
if ($account = collection::search($arangodb->session, sprintf("FOR d IN account FILTER d.id == '%s' RETURN d", $id))) {
|
||||||
|
// Найден аккаунт
|
||||||
|
|
||||||
|
// Возврат (успех)
|
||||||
|
return $account;
|
||||||
|
} else if ($registration) {
|
||||||
|
// Не найден аккаунт и запрошена его регистрация
|
||||||
|
|
||||||
|
// Создание аккаунта
|
||||||
|
document::write($arangodb->session, 'account', ['id' => $id, 'banned' => false, 'settings' => false, 'version' => VERSION]);
|
||||||
|
|
||||||
|
// Авторизация (без регистрации)
|
||||||
|
return authorization($id, false);
|
||||||
|
} else {
|
||||||
|
// Не найден аккаунт и не запрошена его регистрация
|
||||||
|
|
||||||
|
// Выход (провал)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else throw new exception('Не удалось инициализировать коллекцию: account');
|
||||||
|
|
||||||
|
// Выход (провал)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Главное меню
|
||||||
|
*
|
||||||
|
* Команда: /start
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function menu(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Инициализация клавиатуры
|
||||||
|
$keyboard = [
|
||||||
|
[
|
||||||
|
['text' => '🛒 Каталог']
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['text' => '💬 Контакты'],
|
||||||
|
['text' => '🏛️ О компании']
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['text' => '🎯 Сообщество']
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
if ($ctx->get('account')?->settings) $keyboard[] = [['text' => '⚙️ Настройки']];
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(
|
||||||
|
unmarkdown(<<<TXT
|
||||||
|
Это сообщение будет отображаться (оно должно быть обязательно) при вызове главного меню командой /start (создаёт кнопки меню снизу)
|
||||||
|
TXT),
|
||||||
|
[
|
||||||
|
'reply_markup' => [
|
||||||
|
'keyboard' => $keyboard,
|
||||||
|
'resize_keyboard' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Категории
|
||||||
|
*
|
||||||
|
* Команда: /catalog
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function categories(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown(<<<TXT
|
||||||
|
Выберите категорию
|
||||||
|
TXT), [
|
||||||
|
'reply_markup' => [
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
['text' => '🗜️ Тюнинг', 'callback_data' => 'tuning']
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Тюнинг
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function tuning(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(<<<TXT
|
||||||
|
Выберите запчасть
|
||||||
|
TXT, [
|
||||||
|
'reply_markup' => [
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
['text' => 'Цевьё', 'callback_data' => 'brands']
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Бренды
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function brands(Context $ctx): void
|
||||||
|
{
|
||||||
|
if ($spare = spares($ctx->getMessage()->getText())) {
|
||||||
|
// Инициализирована запчасть
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(<<<TXT
|
||||||
|
Выберите бренд
|
||||||
|
TXT, [
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
])->then(function ($message) use ($ctx) {
|
||||||
|
$ctx;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Не инициализирована запчасть
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage('⚠️ *Не найдена запчасть*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Контакты
|
||||||
|
*
|
||||||
|
* Команда: /contacts
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function contacts(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown(<<<TXT
|
||||||
|
Здесь придумать текст для раздела "Контакты"
|
||||||
|
TXT), [
|
||||||
|
'reply_markup' => [
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
['text' => '⚡ Связь с менеджером', 'url' => 'https://t.me/iarming'],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['text' => '📨 Почта', 'callback_data' => 'mail']
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['text' => '🪖 Сайт', 'url' => 'https://arming.ru'],
|
||||||
|
['text' => '🛒 Wildberries', 'url' => 'https://arming.ru']
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Почта
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function _mail(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown(<<<TXT
|
||||||
|
[info@arming.ru](mailto::info@arming.ru)
|
||||||
|
TXT, ['[', ']', '(', ')']), [
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Компания
|
||||||
|
*
|
||||||
|
* Команда: /company
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function company(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(
|
||||||
|
unmarkdown(<<<TXT
|
||||||
|
Здесь придумать текст для раздела "Компания"
|
||||||
|
TXT),
|
||||||
|
/* [
|
||||||
|
'reply_markup' => [
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
['text' => '⚡ Связь с менеджером', 'url' => 'https://git.mirzaev.sexy/mirzaev/mashtrash'],
|
||||||
|
['text' => '📨 Почта', 'text' => ''],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['text' => '🪖 Сайт', 'url' => '']
|
||||||
|
['text' => '🛒 Wildberries', 'url' => '']
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
]
|
||||||
|
] */
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сообщество
|
||||||
|
*
|
||||||
|
* Команда: /community
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function community(Context $ctx): void
|
||||||
|
{
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown(<<<TXT
|
||||||
|
Здесь придумать текст для раздела "Сообщество"
|
||||||
|
TXT), [
|
||||||
|
'reply_markup' => [
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
['text' => '💬 Основной чат', 'url' => 'https://t.me/arming_zone'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Настройки (доступ только авторизованным)
|
||||||
|
*
|
||||||
|
* Команда: /settings
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function settings(Context $ctx): void
|
||||||
|
{
|
||||||
|
if ($ctx->get('account')?->settings) {
|
||||||
|
// Авторизован доступ к настройкам
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(
|
||||||
|
unmarkdown(<<<TXT
|
||||||
|
Панель управления чат-роботом ARMING
|
||||||
|
TXT),
|
||||||
|
[
|
||||||
|
'reply_markup' => [
|
||||||
|
'inline_keyboard' => [
|
||||||
|
[
|
||||||
|
['text' => '📦 Импорт товаров', 'callback_data' => 'import_request'],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'link_preview_options' => [
|
||||||
|
'is_disabled' => true
|
||||||
|
],
|
||||||
|
'disable_notification' => true
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Не авторизован доступ к настройкам
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage('⛔ *Нет доступа*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Запросить файл для импорта товаров (доступ только авторизованным)
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function import_request(Context $ctx): void
|
||||||
|
{
|
||||||
|
if ($ctx->get('account')?->settings) {
|
||||||
|
// Авторизован доступ к настройкам
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown('Отправьте документ в формате xlsx со списком товаров'))
|
||||||
|
->then(function ($message) use ($ctx) {
|
||||||
|
// Отправка файла
|
||||||
|
$ctx->sendDocument(new InputFile(CATALOG_EXAMPLE), ['disable_notification' => true]);
|
||||||
|
|
||||||
|
// Импорт файла
|
||||||
|
$ctx->nextStep("import");
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Не авторизован доступ к настройкам
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage('⛔ *Нет доступа*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Импорт товаров (доступ только авторизованным)
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function import(Context $ctx): void
|
||||||
|
{
|
||||||
|
if ($ctx->get('account')?->settings) {
|
||||||
|
// Авторизован доступ к настройкам
|
||||||
|
|
||||||
|
// Инициализация документа
|
||||||
|
$document = $ctx->getMessage()?->getDocument();
|
||||||
|
|
||||||
|
if ($document instanceof telegram_document) {
|
||||||
|
// Инициализирован документ
|
||||||
|
|
||||||
|
// Инициализация файла
|
||||||
|
$ctx->getFile($document->getFileId())->then(function ($file) use ($ctx) {
|
||||||
|
|
||||||
|
if ($file->getFileSize() <= 50000000) {
|
||||||
|
// Не превышает 50 мегабайт (50000000 байт) размер файла
|
||||||
|
|
||||||
|
if ($file->getFilePath()['extension'] === 'xlsx') {
|
||||||
|
// Имеет расширение xlsx файл
|
||||||
|
|
||||||
|
// Сохранение файла
|
||||||
|
file_put_contents(STORAGE . DIRECTORY_SEPARATOR . 'import.xlsx', file_get_contents('https://api.telegram.org/file/bot' . KEY . '/' . $file->getFilePath()));
|
||||||
|
|
||||||
|
// Инициализация счётчика загруженных товаров
|
||||||
|
$loaded = $created = $updated = $deleted = $old = $new = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(<<<TXT
|
||||||
|
*Загружено для обработки:* $loaded
|
||||||
|
|
||||||
|
*Добавлено:* $created
|
||||||
|
*Обновлено:* $updated
|
||||||
|
*Удалено:* $deleted
|
||||||
|
|
||||||
|
*Опубликовано в магазине:* $old \-\> *$new*
|
||||||
|
TXT)
|
||||||
|
->then(function ($message) use ($ctx) {
|
||||||
|
// Завершение диалога
|
||||||
|
$ctx->endConversation();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Не имеет расширение xlsx файл
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown('Файл должен иметь расширение xlsx'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Превышает 50 мегабайт (50000000 байт) размер файла
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown('Размер файла не должен превышать 50 мегабайт'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Не инициализирован документ
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage(unmarkdown('Отправьте документ в формате xlsx со списком товаров'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Не авторизован доступ к настройкам
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage('⛔ *Нет доступа*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = new Config();
|
||||||
|
$config->setParseMode(Config::PARSE_MODE_MARKDOWN);
|
||||||
|
$config->useReactFileSystem(true);
|
||||||
|
|
||||||
|
$bot = new Zanzara(KEY, $config);
|
||||||
|
$bot->onCommand('start', function (Context $ctx): void {
|
||||||
|
// Главное меню
|
||||||
|
menu($ctx);
|
||||||
|
});
|
||||||
|
|
||||||
|
/* $bot->onUpdate(function (Context $ctx): void {}); */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Инициализация аккаунта (middleware)
|
||||||
|
*
|
||||||
|
* @param Context $ctx
|
||||||
|
* @param Node $next
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
$account = function (Context $ctx, Node $next): void {
|
||||||
|
// Выполнение заблокировано?
|
||||||
|
if ($ctx->get('stop')) return;
|
||||||
|
|
||||||
|
// Авторизация аккаунта
|
||||||
|
$account = authorization($ctx->getEffectiveUser()->getId());
|
||||||
|
|
||||||
|
if ($account instanceof _document) {
|
||||||
|
// Инициализирован аккаунт (подразумевается)
|
||||||
|
|
||||||
|
if ($account->banned) {
|
||||||
|
// Заблокирован аккаунт
|
||||||
|
|
||||||
|
// Отправка сообщения
|
||||||
|
$ctx->sendMessage('⛔ *Ты заблокирован*');
|
||||||
|
|
||||||
|
// Завершение диалога
|
||||||
|
$ctx->endConversation();
|
||||||
|
|
||||||
|
// Блокировка дальнейшего выполнения
|
||||||
|
$ctx->set('stop', true);
|
||||||
|
} else {
|
||||||
|
// Не заблокирован аккаунт
|
||||||
|
|
||||||
|
// Запись в буфер
|
||||||
|
$ctx->set('account', $account);
|
||||||
|
|
||||||
|
// Продолжение выполнения
|
||||||
|
$next($ctx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Не инициализирован аккаунт
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$bot->onCommand('catalog', fn($ctx) => categories($ctx));
|
||||||
|
$bot->onCommand('contacts', fn($ctx) => contacts($ctx));
|
||||||
|
$bot->onCommand('company', fn($ctx) => company($ctx));
|
||||||
|
$bot->onCommand('community', fn($ctx) => community($ctx));
|
||||||
|
$bot->onCommand('settings', fn($ctx) => settings($ctx));
|
||||||
|
|
||||||
|
$bot->onText('🛒 Каталог', fn($ctx) => categories($ctx));
|
||||||
|
$bot->onText('💬 Контакты', fn($ctx) => contacts($ctx));
|
||||||
|
$bot->onText('🏛️ О компании', fn($ctx) => company($ctx));
|
||||||
|
$bot->onText('🎯 Сообщество', fn($ctx) => community($ctx));
|
||||||
|
$bot->onText('⚙️ Настройки', fn($ctx) => settings($ctx));
|
||||||
|
|
||||||
|
$bot->onCbQueryData(['mail'], fn($ctx) => _mail($ctx));
|
||||||
|
$bot->onCbQueryData(['import_request'], fn($ctx) => import_request($ctx));
|
||||||
|
$bot->onCbQueryData(['tuning'], fn($ctx) => tuning($ctx));
|
||||||
|
$bot->onCbQueryData(['brands'], fn($ctx) => brands($ctx));
|
||||||
|
|
||||||
|
$bot->middleware($account)->run();
|
Binary file not shown.
Binary file not shown.
|
@ -1,675 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// Фреймворк ArangoDB
|
|
||||||
use mirzaev\arangodb\connection,
|
|
||||||
mirzaev\arangodb\collection,
|
|
||||||
mirzaev\arangodb\document;
|
|
||||||
|
|
||||||
// Библиотека для ArangoDB
|
|
||||||
use ArangoDBClient\Document as _document,
|
|
||||||
ArangoDBClient\Cursor,
|
|
||||||
ArangoDBClient\Statement as _statement;
|
|
||||||
|
|
||||||
// Фреймворк Telegram
|
|
||||||
use Zanzara\Zanzara;
|
|
||||||
use Zanzara\Context;
|
|
||||||
use Zanzara\Config;
|
|
||||||
|
|
||||||
require __DIR__ . '/../../../../../../../vendor/autoload.php';
|
|
||||||
|
|
||||||
$arangodb = new connection(require __DIR__ . '/../settings/arangodb.php');
|
|
||||||
|
|
||||||
/* ini_set('error_reporting', E_ALL);
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1); */
|
|
||||||
|
|
||||||
function escape(string $text)
|
|
||||||
{
|
|
||||||
return str_replace(
|
|
||||||
[
|
|
||||||
'#',
|
|
||||||
'*',
|
|
||||||
'_',
|
|
||||||
'=',
|
|
||||||
'.',
|
|
||||||
'[',
|
|
||||||
']',
|
|
||||||
'(',
|
|
||||||
')',
|
|
||||||
'-',
|
|
||||||
'>',
|
|
||||||
'<'
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'\#',
|
|
||||||
'\*',
|
|
||||||
'\_',
|
|
||||||
'\\=',
|
|
||||||
'\.',
|
|
||||||
'\[',
|
|
||||||
'\]',
|
|
||||||
'\(',
|
|
||||||
'\)',
|
|
||||||
'\-',
|
|
||||||
'\>',
|
|
||||||
'\<'
|
|
||||||
],
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Авторизация
|
|
||||||
*
|
|
||||||
* @param string $id Идентификатор Telegram
|
|
||||||
*
|
|
||||||
* @return _document|null|false (инстанция аккаунта, если подключен и авторизован; null, если не подключен; false, если подключен но неавторизован)
|
|
||||||
*/
|
|
||||||
function authorization(string $id): _document|null|false
|
|
||||||
{
|
|
||||||
global $arangodb;
|
|
||||||
|
|
||||||
if (collection::init($arangodb->session, 'telegram')) {
|
|
||||||
if ($telegram = collection::search($arangodb->session, sprintf("FOR d IN telegram FILTER d.id == '%s' RETURN d", $id))) {
|
|
||||||
if ($telegram->number === null) return null;
|
|
||||||
else if (
|
|
||||||
$telegram->active
|
|
||||||
&& collection::init($arangodb->session, 'account')
|
|
||||||
&& $account = collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
"FOR d IN account FILTER d.number == '%s' RETURN d",
|
|
||||||
$telegram->number,
|
|
||||||
$telegram->getId()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) return $account;
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
} else throw new exception('Не удалось инициализировать коллекцию');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сотрудник
|
|
||||||
*
|
|
||||||
* @param string $id Идентификатор аккаунта
|
|
||||||
*
|
|
||||||
* @return _document|null|false (инстанция аккаунта, если подключен и авторизован; null, если не подключен; false, если подключен но неавторизован)
|
|
||||||
*/
|
|
||||||
function worker(string $id): _document|null|false
|
|
||||||
{
|
|
||||||
global $arangodb;
|
|
||||||
|
|
||||||
return collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
<<<'AQL'
|
|
||||||
FOR d IN worker
|
|
||||||
LET e = (
|
|
||||||
FOR e IN account_edge_worker
|
|
||||||
FILTER e._from == '%s'
|
|
||||||
SORT e.created DESC, e._key DESC
|
|
||||||
LIMIT 1
|
|
||||||
RETURN e
|
|
||||||
)
|
|
||||||
FILTER d._id == e[0]._to
|
|
||||||
SORT d.created DESC, d._key DESC
|
|
||||||
LIMIT 1
|
|
||||||
RETURN d
|
|
||||||
AQL,
|
|
||||||
$id
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function registration(string $id, string $number): bool
|
|
||||||
{
|
|
||||||
global $arangodb;
|
|
||||||
|
|
||||||
if (collection::init($arangodb->session, 'telegram')) {
|
|
||||||
if ($telegram = collection::search($arangodb->session, sprintf("FOR d IN telegram FILTER d.id == '%s' RETURN d", $id))) {
|
|
||||||
// Найден аккаунт
|
|
||||||
|
|
||||||
// Запись номера
|
|
||||||
$telegram->number = $number;
|
|
||||||
if (!document::update($arangodb->session, $telegram)) return false;
|
|
||||||
} else if (
|
|
||||||
$number === null
|
|
||||||
|| !$telegram = collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
"FOR d IN telegram FILTER d._id == '%s' RETURN d",
|
|
||||||
document::write($arangodb->session, 'telegram', ['id' => $id, 'active' => false, 'number' => $number])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
) return false;
|
|
||||||
|
|
||||||
// Инициализация ребра: account -> telegram
|
|
||||||
if (
|
|
||||||
collection::init($arangodb->session, 'account')
|
|
||||||
&& ($account = collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
"FOR d IN account FILTER d.number == '%d' RETURN d",
|
|
||||||
$telegram->number
|
|
||||||
)
|
|
||||||
))
|
|
||||||
&& collection::init($arangodb->session, 'connection', true)
|
|
||||||
&& (collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
"FOR d IN connection FILTER d._from == '%s' && d._to == '%s' RETURN d",
|
|
||||||
$account->getId(),
|
|
||||||
$telegram->getId()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
?? collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
"FOR d IN connection FILTER d._id == '%s' RETURN d",
|
|
||||||
document::write(
|
|
||||||
$arangodb->session,
|
|
||||||
'connection',
|
|
||||||
['_from' => $account->getId(), '_to' => $telegram->getId()]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
) {
|
|
||||||
// Инициализировано ребро: account -> telegram
|
|
||||||
|
|
||||||
// Активация
|
|
||||||
$telegram->active = true;
|
|
||||||
return document::update($arangodb->session, $telegram);
|
|
||||||
}
|
|
||||||
} else throw new exception('Не удалось инициализировать коллекцию');
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateAuthenticationKeyboard(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'reply_markup' => [
|
|
||||||
'keyboard' => [
|
|
||||||
[
|
|
||||||
['text' => '🔐 Аутентификация', 'request_contact' => true]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'resize_keyboard' => true
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateMenu(Context $ctx): void
|
|
||||||
{
|
|
||||||
if ($account = authorization($ctx->getMessage()?->getFrom()?->getId() ?? $ctx->getCallbackQuery()->getFrom()->getId())) {
|
|
||||||
// Успешная авторизация
|
|
||||||
|
|
||||||
if (!$account->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($account->banned) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if (!($worker = worker($account->getId()))->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($worker->fired) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else {
|
|
||||||
// Активен аккаунт
|
|
||||||
|
|
||||||
$ctx->sendMessage('👋 Здравствуйте, ' . preg_replace('/([._\-()!#])/', '\\\$1', $account->name['first']), [
|
|
||||||
'reply_markup' => [
|
|
||||||
'inline_keyboard' => [
|
|
||||||
[
|
|
||||||
['text' => '🔍 Активные заявки', 'callback_data' => 'day']
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'remove_keyboard' => true
|
|
||||||
]
|
|
||||||
])->then(function ($message) use ($ctx) {
|
|
||||||
$ctx->setChatDataItem("menu", $message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Прочитать заявки из ArangoDB
|
|
||||||
*
|
|
||||||
* @param int $amount Количество
|
|
||||||
* @param ?string $date За какую дату (unixtime)
|
|
||||||
* @param int $page Страница
|
|
||||||
* @param _document $worker Сотрудник
|
|
||||||
*
|
|
||||||
* @return Cursor
|
|
||||||
*/
|
|
||||||
function requests(int $amount = 5, ?string $date = null, int $page = 1, _document $worker): Cursor
|
|
||||||
{
|
|
||||||
global $arangodb;
|
|
||||||
|
|
||||||
// Инициализация значения даты по умолчанию
|
|
||||||
$date ??= time();
|
|
||||||
|
|
||||||
// Фильтрация номера страницы
|
|
||||||
if ($page < 1) $page = 1;
|
|
||||||
|
|
||||||
// Инициализация номера страницы для вычислний
|
|
||||||
--$page;
|
|
||||||
|
|
||||||
// Инициализация сдвига
|
|
||||||
$offset = $page === 0 ? 0 : $page * $amount;
|
|
||||||
|
|
||||||
return (new _statement(
|
|
||||||
$arangodb->session,
|
|
||||||
[
|
|
||||||
'query' => sprintf(
|
|
||||||
// d.date < %s там специально, не менять на <=
|
|
||||||
"FOR d IN task FILTER ((d.date >= %s && d.date < %s && d.start >= '05:00') || (d.date >= %s && d.date < %s && d.start < '05:00')) && d.worker == null && d.market != null && d.confirmed != true && d.published == true && d.completed != true && (FOR m IN market FILTER m.id == d.market && IS_ARRAY(m.bans) SORT m.created DESC, m._key DESC LIMIT 1 RETURN !POSITION(m.bans, \"%s\"))[0] SORT d.created DESC, d._key DESC LIMIT %d, %d RETURN d",
|
|
||||||
$from = (new DateTime("@$date"))->setTime(0, 0)->format('U'),
|
|
||||||
$to = (new DateTime("@$date"))->modify('+1 day')->setTime(0, 0)->format('U'),
|
|
||||||
$to,
|
|
||||||
(new DateTime("@$date"))->modify('+2 day')->setTime(0, 0)->format('U'),
|
|
||||||
$worker->id,
|
|
||||||
$offset,
|
|
||||||
$amount + $offset - ($page > 0)
|
|
||||||
),
|
|
||||||
"batchSize" => 1000,
|
|
||||||
"sanitize" => true
|
|
||||||
]
|
|
||||||
))->execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateEmojis(): string
|
|
||||||
{
|
|
||||||
return '&#' . hexdec(trim(array_rand(file(__DIR__ . '/../emojis.txt')))) . ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
function requests_next(Context $ctx): void
|
|
||||||
{
|
|
||||||
$ctx->getChatDataItem('requests_page')->then(function ($page) use ($ctx) {
|
|
||||||
$ctx->setChatDataItem('requests_page', ($page ?? 1) + 1)->then(function () use ($ctx, $page) {
|
|
||||||
search($ctx);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function requests_previous(Context $ctx): void
|
|
||||||
{
|
|
||||||
$ctx->getChatDataItem('requests_page')->then(function ($page) use ($ctx) {
|
|
||||||
$ctx->setChatDataItem('requests_page', ($page ?? 2) - 1)->then(function () use ($ctx) {
|
|
||||||
search($ctx);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function request_choose(Context $ctx): void
|
|
||||||
{
|
|
||||||
if (($account = authorization($ctx->getCallbackQuery()->getFrom()->getId())) instanceof _document) {
|
|
||||||
// Авторизован
|
|
||||||
|
|
||||||
if (!$account->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($account->banned) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if (!($worker = worker($account->getId()))->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($worker->fired) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else {
|
|
||||||
// Активен аккаунт
|
|
||||||
|
|
||||||
// Инициализация ключа инстанции task в базе данных
|
|
||||||
preg_match('/\->\s#(\d+)\n/', $ctx->getCallbackQuery()->getMessage()->getText(), $matches);
|
|
||||||
|
|
||||||
// Запись ключа инстанции task (заявка на которую регистрируется сотрудник)
|
|
||||||
$ctx->setChatDataItem("request_confirmation_target", $matches[1]);
|
|
||||||
|
|
||||||
// Запрос подтверждения
|
|
||||||
$ctx->sendMessage("⚡ *Подтверждение записи*\n\n" . preg_replace('/(^[^:\s\n\r]+:)/m', '*$1*', preg_replace('/(\\\#\d+)/', '*$1*', escape($ctx->getCallbackQuery()->getMessage()->getText()))) . "\n\n*⚠️ Вы подтверждаете отправку запроса?*", [
|
|
||||||
'reply_markup' => [
|
|
||||||
'inline_keyboard' => [
|
|
||||||
[
|
|
||||||
['text' => 'Подтвердить', 'callback_data' => 'request_confirmed'],
|
|
||||||
['text' => 'Отменить', 'callback_data' => 'request_rejected']
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
])->then(function ($message) use ($ctx) {
|
|
||||||
// Запись сообщения в кеш (на случай необходимости его удаления)
|
|
||||||
$ctx->setChatDataItem("request_confirmation", $message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function request_confirmed(Context $ctx): void
|
|
||||||
{
|
|
||||||
global $arangodb;
|
|
||||||
|
|
||||||
if (($account = authorization($ctx->getCallbackQuery()->getFrom()->getId())) instanceof _document) {
|
|
||||||
// Авторизован
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("request_confirmation_target")->then(function ($_key) use ($ctx, $arangodb, $account) {
|
|
||||||
// Прочитана запрашиваемая заявка
|
|
||||||
|
|
||||||
// Инициализация инстанции task в базе данных (выбранного задания)
|
|
||||||
$task = collection::search($arangodb->session, sprintf("FOR d IN task FILTER d._key == '%s' && d.published == true && d.completed != true && worker == null RETURN d", $_key));
|
|
||||||
|
|
||||||
if ($task instanceof _document) {
|
|
||||||
// Найдена заявка (подразумевается, что не занята)
|
|
||||||
|
|
||||||
if ($worker ??= worker($account->getId())) {
|
|
||||||
// Найден сотрудник
|
|
||||||
|
|
||||||
// Запись идентификатора нового сотрудника
|
|
||||||
$task->worker = $worker->id;
|
|
||||||
|
|
||||||
// Снятие с публикации
|
|
||||||
$task->published = false;
|
|
||||||
|
|
||||||
if (document::update($arangodb->session, $task)) {
|
|
||||||
// Записано обновление в базу данных
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("request_all")->then(function ($requests = []) use ($ctx) {
|
|
||||||
// Удаление сообщений связанных с запросом
|
|
||||||
foreach ($requests ?? [] as $_message) $ctx->deleteMessage($_message->getChat()->getId(), $_message->getMessageId());
|
|
||||||
});
|
|
||||||
$ctx->setChatDataItem("request_all", []);
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("request_confirmation")->then(function ($message) use ($ctx) {
|
|
||||||
$ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
});
|
|
||||||
$ctx->setChatDataItem("request_confirmation_target", null);
|
|
||||||
|
|
||||||
$ctx->sendMessage("✅ *Вы зарегистрировались на заявку:* \#$_key", ['reply_markup' => ['remove_keyboard' => true]])->then(function () use ($ctx) {
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
|
|
||||||
// End of the process
|
|
||||||
$ctx->endConversation();
|
|
||||||
} else $ctx->sendMessage("❎ *Не удалось принять заявку:* \#$_key", ['reply_markup' => ['remove_keyboard' => true]])->then(function () use ($ctx) {
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
} else $ctx->sendMessage("❎ *Не удалось принять заявку:* \#$_key", ['reply_markup' => ['remove_keyboard' => true]])->then(function () use ($ctx) {
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
} else $ctx->sendMessage("❎ *Не удалось принять заявку:* \#$_key", ['reply_markup' => ['remove_keyboard' => true]])->then(function () use ($ctx) {
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function request_rejected(Context $ctx): void
|
|
||||||
{
|
|
||||||
$ctx->getChatDataItem("request_confirmation_target")->then(function ($_key) use ($ctx) {
|
|
||||||
// Прочитана запрашиваемая заявка
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("request_confirmation")->then(function ($message) use ($ctx) {
|
|
||||||
$ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
});
|
|
||||||
$ctx->setChatDataItem("request_confirmation_target", null);
|
|
||||||
|
|
||||||
$ctx->sendMessage("✅ *Вы отменили регистрацию на заявку:* \#$_key", ['reply_markup' => ['remove_keyboard' => true]])->then(function () use ($ctx) {
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
|
|
||||||
// End of the process
|
|
||||||
$ctx->endConversation();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function day(Context $ctx): void
|
|
||||||
{
|
|
||||||
if (($account = authorization($ctx->getMessage()?->getFrom()?->getId() ?? $ctx->getCallbackQuery()->getFrom()->getId())) instanceof _document) {
|
|
||||||
// Авторизован
|
|
||||||
|
|
||||||
if (!$account->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($account->banned) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if (!($worker = worker($account->getId()))->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($worker->fired) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else {
|
|
||||||
// Активен аккаунт
|
|
||||||
|
|
||||||
// Инициализация буфера клавиатуры
|
|
||||||
$keyboard = [];
|
|
||||||
|
|
||||||
// Генерация кнопок с выбором даты
|
|
||||||
for ($i = 1, $r = 0; $i < 15; ++$i) $keyboard[$i > 4 * ($r + 1) ? ++$r : $r][] = ['text' => ($date = (new DateTime)->modify("+$i day"))->format('d.m.Y'), 'callback_data' => $date->format('U')];
|
|
||||||
|
|
||||||
$ctx->setChatDataItem('requests_page', 1)->then(function () use ($ctx, $keyboard) {
|
|
||||||
// Отправка меню
|
|
||||||
$ctx->sendMessage('📅 Выберите дату', [
|
|
||||||
'reply_markup' => [
|
|
||||||
'inline_keyboard' => $keyboard
|
|
||||||
]
|
|
||||||
])->then(function ($message) use ($ctx) {
|
|
||||||
$ctx->getChatDataItem("menu")->then(function ($message) use ($ctx) {
|
|
||||||
// Удаление главного меню
|
|
||||||
if ($message) $ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("menu", null);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Запись сообщения в кеш (на случай необходимости его удаления при смене страницы)
|
|
||||||
$ctx->setChatDataItem("request_day", $message);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctx->nextStep("search");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function search(Context $ctx): void
|
|
||||||
{
|
|
||||||
global $arangodb;
|
|
||||||
|
|
||||||
if (($account = authorization($ctx->getMessage()?->getFrom()?->getId() ?? $ctx->getCallbackQuery()->getFrom()->getId())) instanceof _document) {
|
|
||||||
// Авторизован
|
|
||||||
|
|
||||||
if (!$account->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($account->banned) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if (!($worker = worker($account->getId()))->active) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else if ($worker->fired) $ctx->sendMessage('⚠️ Свяжитесь с оператором');
|
|
||||||
else {
|
|
||||||
// Активен аккаунт
|
|
||||||
|
|
||||||
$ctx->getChatDataItem('requests_page')->then(function ($page) use ($ctx, $arangodb, $worker) {
|
|
||||||
// Найдена текущая страница
|
|
||||||
|
|
||||||
// Значение страницы по умолчанию
|
|
||||||
if (empty($page)) {
|
|
||||||
$page = 1;
|
|
||||||
$ctx->setChatDataItem('requests_page', 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$generate = function ($date) use ($ctx, $page, $arangodb, $worker) {
|
|
||||||
// Поиск заявок в ArangoDB
|
|
||||||
$tasks = requests(4, (string) $date, $page, $worker);
|
|
||||||
|
|
||||||
// Подсчёт количества прочитанных заявок из базы данных
|
|
||||||
$count = $tasks->getCount();
|
|
||||||
|
|
||||||
// Проверка существования избытка
|
|
||||||
$excess = $count > 3;
|
|
||||||
|
|
||||||
// Обрезка заявок до размера страницы (3 заявки на 1 странице)
|
|
||||||
$tasks = array_slice($tasks->getAll(), 0, 3);
|
|
||||||
|
|
||||||
if ($count === 0) {
|
|
||||||
$ctx->sendMessage('📦 *Заявок нет*')->then(function ($message) use ($ctx) {
|
|
||||||
$ctx->getChatDataItem("request_all")->then(function ($requests = []) use ($ctx, $message) {
|
|
||||||
// Удаление сообщений связанных с запросом
|
|
||||||
foreach ($requests ?? [] as $_message) $ctx->deleteMessage($_message->getChat()->getId(), $_message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_all", $requests = [$message]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Найдены заявки
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("request_day")->then(function ($message) use ($ctx, $arangodb, $tasks, $page, $excess) {
|
|
||||||
// Удаление предыдущего меню с выбором даты
|
|
||||||
if ($message) $ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_day", null)->then(function () use ($ctx, $arangodb, $tasks, $page, $excess) {
|
|
||||||
$ctx->getChatDataItem("request_all")->then(function ($requests = []) use ($ctx, $arangodb, $tasks, $excess, $page) {
|
|
||||||
// Удаление сообщений связанных с запросом
|
|
||||||
foreach ($requests ?? [] as $_message) $ctx->deleteMessage($_message->getChat()->getId(), $_message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_all", [])->then(function () use ($ctx, $arangodb, $tasks, $excess, $page) {
|
|
||||||
foreach ($tasks as $i => $task) {
|
|
||||||
// Перебор найденных заявок
|
|
||||||
|
|
||||||
if (($market = collection::search(
|
|
||||||
$arangodb->session,
|
|
||||||
sprintf(
|
|
||||||
"FOR d IN market FILTER d.id == '%s' RETURN d",
|
|
||||||
$task->market
|
|
||||||
)
|
|
||||||
)) instanceof _document) {
|
|
||||||
// Найден магазин
|
|
||||||
$ctx->getChatDataItem("request_$i")->then(function ($message) use ($ctx, $task, $market, $tasks, $i, $page, $excess) {
|
|
||||||
// Удаление предыдущего сообщения на этой позиции
|
|
||||||
if ($message) $ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_$i", null)->then(function () use ($ctx, $task, $market, $tasks, $i, $page, $excess) {
|
|
||||||
// Генерация эмодзи
|
|
||||||
/* $emoji = generateEmojis(); */
|
|
||||||
|
|
||||||
// Отправка сообщения
|
|
||||||
$ctx->sendMessage(
|
|
||||||
preg_replace(
|
|
||||||
'/([._\-()!#])/',
|
|
||||||
'\\\$1',
|
|
||||||
"*#$task->market* -\> *#{$task->getKey()}*\n" . (new DateTime('@' . $task->date))->format('d.m.Y') . " (" . $task->start . " - " . $task->end . ")\n\n*Город:* $market->city\n*Адрес:* $market->address\n*Работа:* $task->work" . (mb_strlen($task->description) > 0 ? "\n\n$task->description" : '')
|
|
||||||
),
|
|
||||||
[
|
|
||||||
'reply_markup' => [
|
|
||||||
'inline_keyboard' => [
|
|
||||||
[
|
|
||||||
['text' => '✅ Отправить запрос', 'callback_data' => 'request_choose']
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
)->then(function ($message) use ($ctx, $tasks, $i, $page, $excess) {
|
|
||||||
// Запись сообщения в кеш (на случай необходимости его удаления при смене страницы)
|
|
||||||
$ctx->setChatDataItem("request_$i", $message)->then(function () use ($ctx, $message, $tasks, $i, $page, $excess) {
|
|
||||||
$ctx->getChatDataItem("request_all")->then(function ($requests = []) use ($ctx, $message, $tasks, $i, $page, $excess) {
|
|
||||||
$ctx->setChatDataItem("request_all", $requests = ($requests ?? []) + [count($requests) => $message])->then(function () use ($ctx, $tasks, $i, $page, $excess) {
|
|
||||||
if ($i === array_key_last($tasks)) {
|
|
||||||
// End of the process
|
|
||||||
$ctx->endConversation();
|
|
||||||
|
|
||||||
// Удаление предыдущего меню
|
|
||||||
$ctx->getChatDataItem("request_menu")->then(function ($message) use ($ctx, $page, $excess) {
|
|
||||||
if ($message) $ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_menu", null)->then(function () use ($ctx, $page, $excess) {
|
|
||||||
// Инициализация буфера для меню поиска
|
|
||||||
$keyboard = [];
|
|
||||||
|
|
||||||
// Генерация кнопки: "Предыдущая страница"
|
|
||||||
if ($page > 1) $keyboard[] = ['text' => 'Назад', 'callback_data' => 'requests_previous'];
|
|
||||||
|
|
||||||
// Генерация кнопки: "Отображённая страница"
|
|
||||||
$keyboard[] = ['text' => $page, 'callback_data' => 'requests_current'];
|
|
||||||
|
|
||||||
// Генерация кнопки: "Следующая страница"
|
|
||||||
if ($excess) $keyboard[] = ['text' => 'Вперёд', 'callback_data' => 'requests_next'];
|
|
||||||
|
|
||||||
// Отправка меню
|
|
||||||
$ctx->sendMessage('🔍 Выберите заявку', [
|
|
||||||
'reply_markup' => [
|
|
||||||
'inline_keyboard' => [
|
|
||||||
$keyboard
|
|
||||||
]
|
|
||||||
]
|
|
||||||
])->then(function ($message) use ($ctx) {
|
|
||||||
// Запись сообщения в кеш (на случай необходимости его удаления при смене страницы)
|
|
||||||
$ctx->setChatDataItem("request_menu", $message);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Инициализация даты и генерация
|
|
||||||
$ctx->getChatDataItem('requests_date')->then(function ($old) use ($ctx, $generate) {
|
|
||||||
$new = $ctx->getCallbackQuery()->getData();
|
|
||||||
if ($new === (string) (int) $new && $new <= PHP_INT_MAX && $new >= ~PHP_INT_MAX) $ctx->setChatDataItem('requests_date', $new)->then(fn () => $generate($new));
|
|
||||||
else $generate($old);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = new Config();
|
|
||||||
$config->setParseMode(Config::PARSE_MODE_MARKDOWN);
|
|
||||||
|
|
||||||
$bot = new Zanzara(require(__DIR__ . '/../settings/key.php'), $config);
|
|
||||||
|
|
||||||
$stop = false;
|
|
||||||
|
|
||||||
$bot->onUpdate(function (Context $ctx) use (&$stop): void {
|
|
||||||
$message = $ctx->getMessage();
|
|
||||||
|
|
||||||
if (
|
|
||||||
isset($message)
|
|
||||||
&& ($contact = $message->getContact())
|
|
||||||
&& $contact->getUserId() === $message->getFrom()->getId()
|
|
||||||
) {
|
|
||||||
// Передан контакт со своими данными (подразумевается второй шаг аутентификации и запуск регистрации)
|
|
||||||
|
|
||||||
// Запуск регистрации
|
|
||||||
if (registration($contact->getUserId(), $contact->getPhoneNumber())) {
|
|
||||||
// Успешная регистрация
|
|
||||||
|
|
||||||
$ctx->sendMessage('✅ *Аккаунт подключен*', ['reply_markup' => ['remove_keyboard' => true]])->then(function () use ($ctx) {
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
|
|
||||||
$stop = true;
|
|
||||||
} else $ctx->sendMessage('⛔ *Вы не авторизованы*', generateAuthenticationKeyboard());
|
|
||||||
} else if ($message?->getText() !== '🔐 Аутентификация' && !authorization($message?->getFrom()?->getId() ?? $ctx->getCallbackQuery()->getFrom()->getId())) {
|
|
||||||
$ctx->sendMessage('⛔ *Вы не авторизованы*', generateAuthenticationKeyboard());
|
|
||||||
|
|
||||||
$stop = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$bot->onCommand('start', function (Context $ctx) use ($stop): void {
|
|
||||||
if ($stop) return;
|
|
||||||
$ctx->getChatDataItem("request_all")->then(function ($requests = []) use ($ctx) {
|
|
||||||
// Удаление сообщений связанных с запросом
|
|
||||||
foreach ($requests ?? [] as $_message) $ctx->deleteMessage($_message->getChat()->getId(), $_message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_all", []);
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("menu")->then(function ($message) use ($ctx) {
|
|
||||||
// Удаление главного меню
|
|
||||||
if ($message) $ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("menu", null);
|
|
||||||
});
|
|
||||||
|
|
||||||
$ctx->getChatDataItem("request_day")->then(function ($message) use ($ctx) {
|
|
||||||
// Удаление меню выбора даты
|
|
||||||
if ($message) $ctx->deleteMessage($message->getChat()->getId(), $message->getMessageId());
|
|
||||||
$ctx->setChatDataItem("request_day", null);
|
|
||||||
});
|
|
||||||
|
|
||||||
generateMenu($ctx);
|
|
||||||
});
|
|
||||||
|
|
||||||
$bot->onCbQueryData(['requests_next'], fn ($ctx) => requests_next($ctx));
|
|
||||||
$bot->onCbQueryData(['requests_previous'], fn ($ctx) => requests_previous($ctx));
|
|
||||||
$bot->onCbQueryData(['request_choose'], fn ($ctx) => request_choose($ctx));
|
|
||||||
$bot->onCbQueryData(['request_confirmed'], fn ($ctx) => request_confirmed($ctx));
|
|
||||||
$bot->onCbQueryData(['request_rejected'], fn ($ctx) => request_rejected($ctx));
|
|
||||||
$bot->onCommand('day', fn ($ctx) => day($ctx));
|
|
||||||
$bot->onCbQueryData(['day'], fn ($ctx) => day($ctx));
|
|
||||||
|
|
||||||
$bot->run();
|
|
Loading…
Reference in New Issue