diff --git a/composer.json b/composer.json index fcc8a1f..429a9c4 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,9 @@ "react/filesystem": "^0.1.2", "nyholm/psr7": "^1.8", "irazasyed/telegram-bot-sdk": "^3.15", - "nutgram/nutgram": "^4.40" + "nutgram/nutgram": "^4.40", + "psr/simple-cache": "^3.0", + "symfony/cache": "^8.0" }, "suggest": { "mirzaev/files": "Easy working with files", diff --git a/composer.lock b/composer.lock index 9eff16b..aefbefd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4cea1a922de2838a239ca7633443750c", + "content-hash": "1f5b64f8a594a24294320f54e2470337", "packages": [ { "name": "badfarm/zanzara", diff --git a/kodorvan/constructor/system/localizations/english.php b/kodorvan/constructor/system/localizations/english.php index 6e727d8..a47df12 100644 --- a/kodorvan/constructor/system/localizations/english.php +++ b/kodorvan/constructor/system/localizations/english.php @@ -25,6 +25,8 @@ return [ 'settings_language_button_add' => 'Add a language', // Authorization + 'authorization_system' => 'System', + 'authorization_settings' => 'Settings', 'not_authorized_system' => 'You do not have access to the system', 'not_authorized_settings' => 'You do not have access to the settings', 'not_authorized_system_settings' => 'You do not have access to the system settings', diff --git a/kodorvan/constructor/system/localizations/russian.php b/kodorvan/constructor/system/localizations/russian.php index 0539024..b38c708 100644 --- a/kodorvan/constructor/system/localizations/russian.php +++ b/kodorvan/constructor/system/localizations/russian.php @@ -22,6 +22,56 @@ return [ 'account_authorized_settings' => 'Доступ к изменению настроек', 'account_authorized_system_settings' => 'Системный доступ к системным настройкам', + // Проект: создание + 'project_create_title' => 'Создание проекта', + 'project_create_description' => 'Получите ориентировочную стоимость всего за 2 минуты\!', + 'project_create_cost' => 'Стоимость', + 'project_create_button_request' => 'Заказать', + + 'project_create_types_title' => 'Выбор типа проекта', + 'project_create_types_description' => 'Каждый тип имеет уникальные параметры и коэффициент', + 'project_create_button_type' => 'Тип проекта', + 'project_create_button_type_selected' => 'Тип', + + 'project_create_purposes_title' => 'Выбор назначения', + 'project_create_purposes_description' => 'Вектор разработки, основание проекта', + 'project_create_button_purpose' => 'Назначение', + 'project_create_button_purpose_selected' => 'Назнач.', + + 'project_create_integrations_title' => 'Выбор интеграций', + 'project_create_integrations_description' => 'Синхронизация, скачивание, загрузка, запись...', + 'project_create_button_integrations' => 'Интеграции', + 'project_create_button_integrations_selected' => 'Интеграции', + + 'project_create_requested' => 'Проект создан и отправлен оператору', + 'project_create_cancelled' => 'Создание проекта отменено', + + // Проект: типы + 'project_type_chat_robot' => 'Чат-робот', + 'project_type_parser' => 'Парсер', + 'project_type_calculator' => 'Калькулятор', + 'project_type_crm' => 'CRM', + 'project_type_site' => 'Сайт', + 'project_type_program' => 'Программа', + 'project_type_complex' => 'Нестандартный', + + // Проект: назначение + 'project_purpose_funnel' => 'Воронка', + 'project_purpose_contact' => 'Контакты', + 'project_purpose_neural_network' => 'Нейросеть', + 'project_purpose_gallery' => 'Галерея', + 'project_purpose_crm' => 'CRM', + 'project_purpose_landing' => 'Лендинг', + 'project_purpose_marketplace' => 'Маркетплейс', + 'project_purpose_charity' => 'Благотворительность', + 'project_purpose_search' => 'Поиск', + 'project_purpose_calculate' => 'Расчёт', + 'project_purpose_tools' => 'Инструменты', + 'project_purpose_workers' => 'Рабочие', + 'project_purpose_objects' => 'Предметы', + 'project_purpose_events' => 'События', + 'project_purpose_special' => 'Особенный', + // Настройки: язык 'settings_language_title' => 'Выбери язык', 'settings_language_description' => 'Выбранный язык будет использоваться для генерации системного отображения', @@ -30,6 +80,8 @@ return [ 'settings_language_button_add' => 'Добавить язык', // Авторизация + 'authorization_system' => 'Система', + 'authorization_settings' => 'Настройки', 'not_authorized_system' => 'У тебя нет доступа к системе', 'not_authorized_settings' => 'У тебя нет доступа к настройкам', 'not_authorized_system_settings' => 'У тебя нет доступа к системным настройкам', diff --git a/kodorvan/constructor/system/models/account.php b/kodorvan/constructor/system/models/account.php index 7b74e2a..1068b6c 100644 --- a/kodorvan/constructor/system/models/account.php +++ b/kodorvan/constructor/system/models/account.php @@ -15,6 +15,9 @@ use kodorvan\constructor\models\core, // The library for languages support use mirzaev\languages\language; +// The library for currencies support +use mirzaev\currencies\currency; + // Baza database use mirzaev\baza\database, mirzaev\baza\column, diff --git a/kodorvan/constructor/system/models/project/enumerations/purpose.php b/kodorvan/constructor/system/models/project/enumerations/purpose.php new file mode 100644 index 0000000..faf3911 --- /dev/null +++ b/kodorvan/constructor/system/models/project/enumerations/purpose.php @@ -0,0 +1,181 @@ + + */ +enum purpose +{ + case funnel; + case contact; + case neural_network; + case gallery; + case crm; + case landing; + case marketplace; + case charity; + case search; + case calculate; + case game; + + case workers; + case tools; + case objects; + case events; + + case special; + + /** + * Label + * + * @param language $language The language + * + * @return string The project type label + */ + public function label(language $language = LANGUAGE_DEFAULT): string + { + // Exit (success) + return match ($this) { + static::funnel => match ($language) { + language::en => 'Funnel', + language::ru => 'Воронка' + }, + static::contact => match ($language) { + language::en => 'Contact', + language::ru => 'Контакты' + }, + static::neural_network => match ($language) { + language::en => 'Neural network', + language::ru => 'Нейросети' + }, + static::game => match ($language) { + language::en => 'Game', + language::ru => 'Игра' + }, + static::gallery => match ($language) { + language::en => 'Gallery', + language::ru => 'Галерея' + }, + static::crm => match ($language) { + default => 'CRM' + }, + static::landing => match ($language) { + language::en => 'Landing', + language::ru => 'Лендинг' + }, + static::marketplace => match ($language) { + language::en => 'Marketplace', + language::ru => 'Маркетплейс' + }, + static::charity => match ($language) { + language::en => 'Charity', + language::ru => 'Благотворительность' + }, + static::search => match ($language) { + language::en => 'Search', + language::ru => 'Поиск' + }, + static::calculate => match ($language) { + language::en => 'Calculate', + language::ru => 'Расчёты' + }, + static::game => match ($language) { + language::en => 'Game', + language::ru => 'Игра' + }, + static::workers => match ($language) { + language::en => 'Workes', + language::ru => 'Рабочие' + }, + static::tools => match ($language) { + language::en => 'Tools', + language::ru => 'Инструменты' + }, + static::objects => match ($language) { + language::en => 'Objects', + language::ru => 'Предметы' + }, + static::events => match ($language) { + language::en => 'Events', + language::ru => 'События' + }, + static::special => match ($language) { + language::en => 'Special', + language::ru => 'Особенный' + } + }; + } + + /** + * Length + * + * @return int Amount of buttons cells length + */ + public function length(): int + { + // Exit (success) + return match ($this) { + static::funnel => 2, + static::contact => 2, + static::neural_network => 3, + static::game => 1, + static::gallery => 1, + static::crm => 1, + static::landing => 1, + static::marketplace => 2, + static::charity => 2, + static::search => 2, + static::calculate => 2, + static::tools => 1, + static::workers => 1, + static::objects => 1, + static::events => 1, + static::special => 4, + default => 1 + }; + } + + /** + * Coefficient + * + * @return int|float Coefficient to the project development cost + */ + public function coefficient(): int|float + { + // Exit (success) + return match ($this) { + static::funnel => 1.4, + static::contact => 1.1, + static::neural_network => 2, + static::game => 3, + static::gallery => 1, + static::crm => 3, + static::landing => 1.2, + static::marketplace => 2, + static::charity => 0.8, + static::search => 1, + static::calculate => 1.1, + static::tools => 1, + static::workers => 1.2, + static::objects => 1, + static::events => 1.5, + static::special => 2, + default => 1 + }; + } +} diff --git a/kodorvan/constructor/system/models/project/enumerations/type.php b/kodorvan/constructor/system/models/project/enumerations/type.php index bb67590..aa17d79 100644 --- a/kodorvan/constructor/system/models/project/enumerations/type.php +++ b/kodorvan/constructor/system/models/project/enumerations/type.php @@ -4,6 +4,15 @@ declare(strict_types=1); namespace kodorvan\constructor\models\project\enumerations; +// Files of the project +use kodorvan\constructor\models\project\enumerations\purpose; + +// The library for languages support +use mirzaev\languages\language; + +// The library for currencies support +use mirzaev\currencies\currency; + // Built-in libraries use InvalidArgumentException as exception_argument, DomainException as exception_domain; @@ -18,13 +27,180 @@ use InvalidArgumentException as exception_argument, */ enum type { - case telegram_voronka; + case chat_robot; case parser; case calculator; case crm; - case marketplace; + /* case marketplace; */ case site; case program; - case special; + case complex; + + /** + * Label + * + * @param language $language The language + * + * @return string The project type label + */ + public function label(language $language = LANGUAGE_DEFAULT): string + { + // Exit (success) + return match ($this) { + static::chat_robot => match ($language) { + language::en => 'Chat-robot', + language::ru => 'Чат-робот' + }, + static::parser => match ($language) { + language::en => 'Parser', + language::ru => 'Парсер' + }, + static::calculator => match ($language) { + language::en => 'Calculator', + language::ru => 'Калькулятор' + }, + static::crm => match ($language) { + default => 'CRM' + }, + /* static::marketplace => match ($language) { + language::en => 'Marketplace', + language::ru => 'Маркетплейс' + }, */ + static::site => match ($language) { + language::en => 'Site', + language::ru => 'Сайт' + }, + static::program => match ($language) { + language::en => 'Program', + language::ru => 'Программа' + }, + static::complex => match ($language) { + language::en => 'Non-standart', + language::ru => 'Нестандартный' + } + }; + } + + /** + * Length + * + * @return int Amount of buttons cells length + */ + public function length(): int + { + // Exit (success) + return match ($this) { + static::chat_robot => 2, + static::parser => 1, + static::calculator => 2, + static::crm => 1, + /* static::marketplace => 4, */ + static::site => 1, + static::program => 1, + static::complex => 2, + default => 1 + }; + } + + /** + * Purposes + * + * @return array Purposes + */ + public function purposes(): array + { + // Exit (success) + return match ($this) { + static::chat_robot => [ + purpose::funnel, + purpose::contact, + purpose::neural_network, + purpose::game, + purpose::gallery, + purpose::crm, + purpose::landing, + purpose::marketplace, + purpose::events, + purpose::charity + ], + static::parser => [ + purpose::search + ], + static::calculator => [ + purpose::calculate + ], + static::crm => [ + purpose::workers, + purpose::tools, + purpose::objects, + purpose::events + ], + static::site => [ + purpose::funnel, + purpose::contact, + purpose::neural_network, + purpose::gallery, + purpose::crm, + purpose::landing, + purpose::marketplace, + purpose::workers, + purpose::tools, + purpose::objects, + purpose::events, + purpose::charity + ], + static::program => [ + purpose::neural_network, + purpose::crm, + purpose::marketplace, + purpose::workers, + purpose::tools, + purpose::objects, + purpose::events, + purpose::charity + ], + default => [] + }; + } + + /** + * Cost + * + * @return int|float The minimal cost of the project development + */ + public function cost(currency $currency = CURRENCY_DEFAULT): int|float + { + // Exit (success) + return match ($this) { + static::chat_robot => match ($currency) { + currency::usd => 20, + currency::rub => 2000 + }, + static::parser => match ($currency) { + currency::usd => 35, + currency::rub => 3500 + }, + static::calculator => match ($currency) { + currency::usd => 40, + currency::rub => 4000 + }, + static::crm => match ($currency) { + currency::usd => 100, + currency::rub => 8000 + }, + static::site => match ($currency) { + currency::usd => 50, + currency::rub => 5000 + }, + static::program => match ($currency) { + currency::usd => 70, + currency::rub => 6000 + }, + static::complex => match ($currency) { + currency::usd => 100, + currency::rub => 10000 + } + }; + } } diff --git a/kodorvan/constructor/system/models/telegram/commands.php b/kodorvan/constructor/system/models/telegram/commands.php deleted file mode 100644 index f3aeb70..0000000 --- a/kodorvan/constructor/system/models/telegram/commands.php +++ /dev/null @@ -1,121 +0,0 @@ - - */ -final class commands extends core -{ - /** - * Account - * - * Responce for the command: "/account" - * - * Sends information about account with menu - * - * @param context $context Request data from Telegram - * - * @return void - */ - public static function account(context $context): void - { - // Initializing the account - $account = $context->get('account'); - - if ($account instanceof account) { - // Initialized the account - - // Initializing localization - $localization = $context->get('localization'); - - if ($localization) { - // Initialized localization - - // Initializing title for the message - $title = '🫵 ' . $localization['account_title']; - - // Declaring buufer of rows about authorizations - $authorizations = ''; - - // Initializing rows about authorization - foreach ($account->values() as $key => $value) { - // Iterating over account parameters - - if (str_starts_with($key, 'authorized_')) { - // Iterating over account authorizations - - // Skipping system authorizations - if (str_starts_with($key, 'authorized_system_')) continue; - - // Writing into buffer of rows about authorizations - $authorizations .= ($value ? '✅' : '❎') . ' *' . ($localization["account_$key"] ?? $key) . ':* ' . ($value ? $localization['yes'] : $localization['no']) . "\n"; - } - } - - // Trimming the last line break character - $authorizations = trim($authorizations, "\n"); - - // Sending the message - $context->sendMessage( - << [ - 'remove_keyboard' => true, - 'disable_notification' => true - ], - 'link_preview_options' => [ - 'is_disabled' => true - ] - ] - ); - } else { - // Not initialized localization - - // Sending the message - $context->sendMessage('⚠️ *Failed to initialize localization*') - ->then(function (message $message) use ($context) { - // Ending the conversation process - $context->endConversation(); - }); - } - } else { - // Not initialized the account - - // Sending the message - $context->sendMessage('⚠️ *Failed to initialize your Telegram account*') - ->then(function (message $message) use ($context) { - // Ending the conversation process - $context->endConversation(); - }); - } - } -} diff --git a/kodorvan/constructor/system/models/telegram/commands/account.php b/kodorvan/constructor/system/models/telegram/commands/account.php new file mode 100644 index 0000000..29f094d --- /dev/null +++ b/kodorvan/constructor/system/models/telegram/commands/account.php @@ -0,0 +1,118 @@ + + */ +final class account extends command +{ + /** + * Command + * + * @var string $name Name of the command + */ + protected string $command = 'account'; + + /** + * Description + * + * @var string $description + */ + protected ?string $description = 'Account profile'; + + /** + * Localizations + * + * Descriptions of the command + * + * @var array $localizedDescriptions + */ + protected array $localizedDescriptions = [ + 'ru' => 'Профиль аккаунта', + '*' => 'Account profile' + ]; + + /** + * Handle + * + * Processing the command + * + * @param telegram $robot The chat-robot instance + * + * @return void + */ + public function handle(telegram $robot): void + { + // Initializing the language + $language = $robot->get('language') ?? LANGUAGE_DEFAULT; + + // Initializing the menu message localization + $localization = $robot->get('localization') ?? new localization($language); + + // Initializing the account + $account = $robot->get('account'); + + // Declaring buufer of rows about authorizations + $authorizations = ''; + + // Initializing rows about authorization + foreach ($account->authorizations()?->record->values() as $key => $value) { + // Iterating over account parameters + + if (match ($key) { + 'identifier', 'account', 'active', 'updated', 'created' => false, + default => true + } && !str_starts_with($key, 'system_')) { + // The value is not metadata and system authorozations + + // Writing into buffer of rows about authorizations + $authorizations .= ($value ? '✅' : '❎') . ' *' . ($localization["authorization_$key"] ?? $key) . ':* ' . ($value ? $localization->yes : $localization->no) . "\n"; + } + } + + // Trimming the last line break character + $authorizations = trim($authorizations, "\n"); + + $robot->sendMessage( + text: implode( + "\n\n", + [ + "🫵 *$localization->account_title*", + $authorizations + ] + ), + parse_mode: mode::MARKDOWN, + disable_notification: true + ); + } +} diff --git a/kodorvan/constructor/system/models/telegram/commands/language.php b/kodorvan/constructor/system/models/telegram/commands/language.php index 1a8c608..0e31088 100644 --- a/kodorvan/constructor/system/models/telegram/commands/language.php +++ b/kodorvan/constructor/system/models/telegram/commands/language.php @@ -119,10 +119,13 @@ final class language extends command // Initializing buffer of languages $languages = type::cases(); - // Deleting the actual language from buffer of languages - unset($languages[array_search($language, $languages, strict: true)]); + // Initializing the selected language index + $selected = array_search($language, $languages, strict: true); - // Sorting buffer of languages by the actual language + // Exclude the selected language from buffer of languages + if ($selected !== false) unset($languages[$selected]); + + // Sorting buffer of languages by the selected language $languages = [$language, ...$languages]; foreach ($languages as $language) { diff --git a/kodorvan/constructor/system/models/telegram/commands/start.php b/kodorvan/constructor/system/models/telegram/commands/start.php index 6079bea..839c64c 100644 --- a/kodorvan/constructor/system/models/telegram/commands/start.php +++ b/kodorvan/constructor/system/models/telegram/commands/start.php @@ -111,7 +111,7 @@ final class start extends command $keyboard->addRow( button::make( text: "📡 $localization->menu_button_operator", - callback_data: 'operator' + url: PROJECT_OPERATOR_URL ?? PROJECT_MEDIA_URL ?? 'https://t.me/kodorvan' ) ); diff --git a/kodorvan/constructor/system/models/telegram/conversations/project/create.php b/kodorvan/constructor/system/models/telegram/conversations/project/create.php new file mode 100644 index 0000000..d1d3ae0 --- /dev/null +++ b/kodorvan/constructor/system/models/telegram/conversations/project/create.php @@ -0,0 +1,924 @@ + + */ +final class create extends menu +{ + /** + * Text + * + * @var string $text The message text + */ + public string $text = ''; + + /** + * Type + * + * @var project_type $type The project type + */ + public project_type $type; + + /** + * Purpose + * + * @var project_purpose $purpose The project purpose + */ + public project_purpose $purpose; + + /** + * Start + * + * Generate the project create menu and start the process + * + * @param telegram $robot The robot + * + * @return void + */ + public function start(telegram $robot): void + { + // Initializing the language + $language = $robot->get('language') ?? LANGUAGE_DEFAULT; + + // Initializing the menu message localization + $localization = $robot->get('localization') ?? new localization($language); + + // Initializing the account + $account = $robot->get('account'); + + // Initializing the project development cost + $cost = $this->cost(); + + // Generating the message text + $text = implode( + "\n\n", + [ + "🏛 *$localization->project_create_title*", + $cost > 0 ? "*$localization->project_create_cost:* " . $cost . ($account->currency?->symbol() ?? CURRENCY_DEFAULT->symbol()) : $localization->project_create_description + ] + ); + + if ($this->text !== $text) { + $this->menuText( + text: $text, + opt: [ + 'parse_mode' => mode::MARKDOWN + ] + ); + } + + if (isset($this->type)) { + // Initialized the project type + + // Initializing the buffer for the first row + $first = []; + + // Writing the project type button into the buffer of the first row + $first[0] = button::make( + text: $localization['project_type_' . $this->type?->name] ?? $this->type?->label(language: $language), + callback_data: '@types' + ); + + if (isset($this->purpose) || $this->type === project_type::complex) { + // Initialized the project purpose + + // Writing the project purpose button into the buffer of the first row + $first[1] = button::make( + text: $localization['project_purpose_' . $this->purpose?->name] ?? $this->purpose?->label(language: $language), + callback_data: '@purposes' + ); + + // Writing the project buttons first row + $this->addButtonRow(...$first); + + // Initializing the row + $row = []; + + // Initializing the maximum amount of buttons in a row + $break = 2; + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Integrations + + if (isset($this->integrations)) { + // Initialized the project integrations + + } else { + // Not initialized the project integrations + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Server + + if (isset($this->server)) { + // Initialized the project server + + } else { + // Not initialized the project server + + } + } + + if (match ($this->type) { + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Interface + + if (isset($this->interface)) { + // Initialized the project interface + + if ($this->type === project_type::calculator) { + // Calculator + + // site, mobile or desktop program + } else if ($this->type === project_type::crm) { + // CRM + + // site, mobile or desktop program + } else if ($this->type === project_type::program) { + // Program + + // mobile or desktop + } + } else { + // Not initialized the project interface + + if ($this->type === project_type::calculator) { + // Calculator + + // site, mobile or desktop program + } else if ($this->type === project_type::crm) { + // CRM + + // site, mobile or desktop program + } else if ($this->type === project_type::program) { + // Program + + // mobile or desktop + } + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Repository + + if (isset($this->repository)) { + // Initialized the project repository + + } else { + // Not initialized the project repository + + } + } + + if (match ($this->type) { + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Testing + + if (isset($this->testing)) { + // Initialized the project testing + + } else { + // Not initialized the project testing + + } + } + + if (match ($this->type) { + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Security + + if (isset($this->security)) { + // Initialized the project security + + } else { + // Not initialized the project security + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Documenting + + if (isset($this->documenting)) { + // Initialized the project documenting + + } else { + // Not initialized the project documenting + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Localization + + if (isset($this->localization)) { + // Initialized the project localization + + } else { + // Not initialized the project localization + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::complex => true, + default => false + }) { + // Journal + + if (isset($this->journal)) { + // Initialized the project journal + + } else { + // Not initialized the project journal + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Scalability + + if (isset($this->scalability)) { + // Initialized the project scalability + + } else { + // Not initialized the project scalability + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::crm, + project_type::site, + project_type::complex => true, + default => false + }) { + // Framework + + if (isset($this->framework)) { + // Initialized the project framework + + } else { + // Not initialized the project framework + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Database + + if (isset($this->database)) { + // Initialized the project database + + } else { + // Not initialized the project database + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Depth of development + + if (isset($this->depth)) { + // Initialized the project depth of development + + } else { + // Not initialized the project depth of development + + } + } + + if (match ($this->type) { + project_type::chat_robot, + project_type::parser, + project_type::calculator, + project_type::crm, + project_type::site, + project_type::program, + project_type::complex => true, + default => false + }) { + // Form of cooperation + + if (isset($this->cooperation)) { + // Initialized the project cooperation + + } else { + // Not initialized the project cooperation + + } + } + } else { + // Not initialized the project purpose + + // Writing the project purpose button into the buffer of the first row + $first[1] = button::make( + text: "🔸 $localization->project_create_button_purpose", + callback_data: '@purposes' + ); + + // Writing the project buttons first row + $this->addButtonRow(...$first); + } + } else { + // Not initialized the project type + + // Writing the project type button + $this->addButtonRow( + button::make( + text: "🔸 $localization->project_create_button_type", + callback_data: '@types' + ) + ); + } + + if ($cost > 0) { + // The project development cost was calculated + + // Writing the project type button + $this->addButtonRow( + button::make( + text: "☑️ $localization->project_create_button_request", + callback_data: '@request' + ) + ); + } + + // Updating the message and saving its text + $this->text = $this->orNext('stop')->showMenu()->text; + } + + /** + * Types + * + * Generate the project type select menu + * + * @param telegram $robot The robot + * + * @return void + */ + public function types(telegram $robot): void + { + // Initializing the language + $language = $robot->get('language') ?? LANGUAGE_DEFAULT; + + // Initializing the menu message localization + $localization = $robot->get('localization') ?? new localization($language); + + // Initializing the account + $account = $robot->get('account'); + + // Updating the message text + $this->menuText( + text: implode( + "\n\n", + [ + "⚙️ *$localization->project_create_types_title*", + $localization->project_create_types_description, + ] + ), + opt: [ + 'parse_mode' => mode::MARKDOWN + ] + ); + + // Deleting the message buttons + $this->clearButtons(); + + // Initializing the row + $row = []; + + // Declaring the buffer of the row buttons length + $length = 0; + + // Initializing the maximum amount of buttons in a row + $break = 4; + + // Initializing buffer of types + $types = project_type::cases(); + + if (isset($this->type)) { + // Initialized the selected type + + // Initializing the selected purpose index + $selected = array_search($this->type ?? null, $types, strict: true); + + if ($selected !== false) { + // Found the selected type index + + // Exclude the selected type from buffer of types + unset($types[$selected]); + } + } + + // Declaring the generated buttons registry + $generated = []; + + foreach ($types as $index => $type) { + // Iterating over types + + if (array_search($type, $generated)) { + // The type button is already generated + + // Skipping the iteration + continue; + } + + if ($length + $type->length() > $break && !empty($row)) { + // Reached the limit of buttons in a row + + // Writing the row into the menu + $this->addButtonRow(...$row); + + // Writing buttons into the generated buttons registry + $generated += $row; + + // Reinitializing the row buttons length + /* $length -= $break; */ + $length = 0; + + // Reinitializing the row + $row = []; + } + + // Addition to row buttons length + $length += $type->length(); + + // Writing the type button into the row + $row[] = button::make( + text: $localization['project_type_' . $type->name] ?? $type->label(language: $language), + callback_data: "$type->name@type" + ); + + // Initializing the next type + $next = $types[$index + 1] ?? null; + + if ($next?->length() >= $break) { + // The next type is the full-length button + + // Writing the row into the menu + $this->addButtonRow(...$row); + + // Writing buttons into the generated buttons registry + $generated += $row; + + // Reinitializing the row + $row = []; + + // Reinitializing the row buttons length + $length = 0; + + // Writing the button into the menu + $this->addButtonRow(button::make( + text: $localization['project_type_' . $next->name] ?? $next->label(language: $language), + callback_data: "$next->name@type" + )); + + // Writing the button into the generated buttons registry + $generated[] = $next; + } + } + + if (!empty($row) > 0) { + // The row was not writed + + // Writing the row into the menu + $this->addButtonRow(...$row); + } + + // Deinitializing deprecated variables + unset($row, $limit, $length, $generated, $types, $type); + + // Updating the message and saving its text + $this->text = $this->showMenu()->text; + } + + /** + * Type + * + * Write the project type + * + * @param telegram $robot The robot + * + * @return void + */ + public function type(telegram $robot): void + { + // Initializing the project type + $this->type = project_type::{$robot->callbackQuery()->data}; + + // Deinitializing the project purpose + unset($this->purpose); + + if (count($this->type->purposes()) === 1) { + // The project type has only 1 purpose + + // Initializing the project purpose + $this->purpose = $this->type->purposes()[0]; + } + + // Deleting the message buttons + $this->clearButtons(); + + // Deleting the message buttons + $this->start(robot: $robot); + } + + /** + * Purposes + * + * Generate the project purpose select menu + * + * @param telegram $robot The robot + * + * @return void + */ + public function purposes(telegram $robot): void + { + // Initializing the language + $language = $robot->get('language') ?? LANGUAGE_DEFAULT; + + // Initializing the menu message localization + $localization = $robot->get('localization') ?? new localization($language); + + // Initializing the account + $account = $robot->get('account'); + + // Updating the message text + $this->menuText( + text: implode( + "\n\n", + [ + "🛠 *$localization->project_create_purposes_title*", + $localization->project_create_purposes_description, + ] + ), + opt: [ + 'parse_mode' => mode::MARKDOWN + ] + ); + + // Deleting the message buttons + $this->clearButtons(); + + // Initializing the row + $row = []; + + // Declaring the buffer of the row buttons length + $length = 0; + + // Initializing the maximum amount of buttons in a row + $break = 4; + + // Initializing buffer of purposes + $purposes = $this->type->purposes(); + + if (isset($this->purpose)) { + // Initialized the selected purpose + + // Initializing the selected purpose index + $selected = array_search($this->purpose ?? null, $purposes, strict: true); + + if ($selected !== false) { + // Found the selected purpose index + + // Exclude the selected purpose from buffer of purposes + unset($purposes[$selected]); + } + } + + // Declaring the generated buttons registry + $generated = []; + + foreach ($purposes as $index => $purpose) { + // Iterating over purposes + + if (array_search($purpose, $generated)) { + // The purpose button is already generated + + // Skipping the iteration + continue; + } + + if ($length + $purpose->length() > $break && !empty($row)) { + // Reached the limit of buttons in a row + + // Writing the row into the menu + $this->addButtonRow(...$row); + + // Writing buttons into the generated buttons registry + $generated += $row; + + // Reinitializing the row buttons length + /* $length -= $break; */ + $length = 0; + + // Reinitializing the row + $row = []; + } + + // Addition to row buttons length + $length += $purpose->length(); + + // Initializing the coefficient + $coefficient = $purpose->coefficient(); + + // Writing the purpose button into the row + $row[] = button::make( + /* text: ($localization['project_purpose_' . $purpose->name] ?? $purpose->label(language: $language)) . (!empty($coefficient) ? ' x' . $coefficient : ''), */ + text: $localization['project_purpose_' . $purpose->name] ?? $purpose->label(language: $language), + callback_data: "$purpose->name@purpose" + ); + + // Initializing the next purpose + $next = $purposes[$index + 1] ?? null; + + if ($next?->length() >= $break) { + // The next purpose is the full-length button + + // Writing the row into the menu + $this->addButtonRow(...$row); + + // Writing buttons into the generated buttons registry + $generated += $row; + + // Reinitializing the row + $row = []; + + // Reinitializing the row buttons length + $length = 0; + + // Initializing the coefficient + $coefficient = $next->coefficient(); + + // Writing the button into the menu + $this->addButtonRow(button::make( + /* text: ($localization['project_purpose_' . $next->name] ?? $next->label(language: $language)) . (!empty($coefficient) ? ' x' . $coefficient : ''), */ + text: $localization['project_purpose_' . $next->name] ?? $next->label(language: $language), + callback_data: "$next->name@purpose" + )); + + // Writing the button into the generated buttons registry + $generated[] = $next; + } + } + + if (!empty($row) > 0) { + // The row was not writed + + // Writing the row into the menu + $this->addButtonRow(...$row); + } + + // Writing the "special" button into the menu + $this->addButtonRow(button::make( + /* text: ($localization->project_purpose_special ?? project_purpose::special->label(language: $language)) . ' x' . project_purpose::special->coefficient(), */ + text: $localization->project_purpose_special ?? project_purpose::special->label(language: $language), + callback_data: project_purpose::special->name . '@purpose' + )); + + // Deinitializing deprecated variables + unset($row, $limit, $length, $generated, $purposes, $purpose); + + // Updating the message and saving its text + $this->text = $this->showMenu()->text; + } + + /** + * Purpose + * + * Write the project purpose + * + * @param telegram $robot The robot + * + * @return void + */ + public function purpose(telegram $robot): void + { + // Initializing the project purpose + $this->purpose = project_purpose::{$robot->callbackQuery()->data}; + + // Deleting the message buttons + $this->clearButtons(); + + // Deleting the message buttons + $this->start(robot: $robot); + } + + /** + * Cost + * + * Calculate the project development cost + * + * @return int|float The project development cost + */ + public function cost(): int|float + { + // Declaring the project development cost + $cost = 0; + + if (isset($this->type)) { + // Initialized the project type + + // Calculating the project development cost + $cost = $this->type->cost(); + } + + if (isset($this->purpose)) { + // Initialized the project purpose + + // Calculating the project development cost + $cost *= $this->purpose->coefficient(); + } + + // Exit (success) + return $cost; + } + + /** + * Request + * + * + * + * @param telegram $robot The robot + * + * @return void + */ + public function request(telegram $robot): void + { + // Initializing the language + $language = $robot->get('language') ?? LANGUAGE_DEFAULT; + + // Initializing the menu message localization + $localization = $robot->get('localization') ?? new localization($language); + + // Sending the message + $robot->sendMessage( + text: "✅ *$localization->project_create_requested*", + parse_mode: mode::MARKDOWN, + disable_notification: true + ); + + // Stopping conversation + $this->end(); + } + + /** + * Stop + * + * Delete the project create menu and terminating the process + * + * @param telegram $robot The robot + * + * @return void + */ + public function stop(telegram $robot): void + { + // Initializing the language + $language = $robot->get('language') ?? LANGUAGE_DEFAULT; + + // Initializing the menu message localization + $localization = $robot->get('localization') ?? new localization($language); + + // Sending the message + $robot->sendMessage( + text: "⚠️ *$localization->project_create_cancelled*", + parse_mode: mode::MARKDOWN, + disable_notification: true + ); + + // Stopping conversation + $this->end(); + } +} diff --git a/kodorvan/constructor/system/models/telegram/middlewares/localization.php b/kodorvan/constructor/system/models/telegram/middlewares/localization.php index 568b5d7..eb6b653 100644 --- a/kodorvan/constructor/system/models/telegram/middlewares/localization.php +++ b/kodorvan/constructor/system/models/telegram/middlewares/localization.php @@ -61,30 +61,14 @@ final class localization extends core if ($language instanceof language) { // Initialized the language - try { - // Initializing the localization - $localization = new model($language); + // Initializing the localization + $localization = new model($language); - // Writing localization into the robot variable - $robot->set('localization', $localization); + // Writing localization into the robot variable + $robot->set('localization', $localization); - // Continuation of the process - $next($robot); - } catch (exception $exception) { - // Not initialized the localization - - // Writing the exception into the errors output buffer - error_log((string) $exception); - - // Sending the message - $robot->sendMessage( - text: '⚠️ *Failed to initialize the localization*', - parse_mode: mode::MARKDOWN - ); - - // Ending the conversation process - $robot->endConversation(); - } + // Continuation of the process + $next($robot); } } } diff --git a/kodorvan/constructor/system/models/telegram/project.php b/kodorvan/constructor/system/models/telegram/project.php new file mode 100644 index 0000000..5c9e030 --- /dev/null +++ b/kodorvan/constructor/system/models/telegram/project.php @@ -0,0 +1,81 @@ + + */ +final class project extends core +{ + /** + * Language + * + * Write the language into the account and the robot instance + * + * @param telegram $robot The chat-robot instance + * @param language $language The language + * + * @return void + */ + public static function create(telegram $robot, language $language = LANGUAGE_DEFAULT): void + { + /* // Initializing the account + $account = $robot->get('account'); + + if ($account instanceof account) { + // Initialized the account + + // Initializing the menu message localization + $localization = new localization($language); + + if ($localization instanceof localization) { + // Initialized the localization + + // Sending the message + $robot->sendMessage( + text: "✅ *$localization->settings_language_update_success:* " . trim($from->flag() . ' ' . $from->label($to)) . ' → *' . trim($to->flag() . ' ' . $to->label($to)) . '*', + parse_mode: mode::MARKDOWN, + disable_notification: true + ); + + // Sending the message + $robot->answerCallbackQuery( + text: $to->label($to), + show_alert: false + ); + } + } */ + } +} diff --git a/kodorvan/constructor/system/public/telegram/constructor/install.php b/kodorvan/constructor/system/public/telegram/constructor/install.php index fa8463b..4595d18 100644 --- a/kodorvan/constructor/system/public/telegram/constructor/install.php +++ b/kodorvan/constructor/system/public/telegram/constructor/install.php @@ -18,8 +18,8 @@ use mirzaev\minimal\core, mirzaev\minimal\route; // Framework for Telegram -/* use Telegram\Bot\BotsManager as telegram; */ use SergiX44\Nutgram\Nutgram as telegram, + SergiX44\Nutgram\Configuration as telegram_settings, SergiX44\Nutgram\RunningMode\Webhook as webhook, SergiX44\Nutgram\Telegram\Types\Internal\InputFile as input; @@ -59,7 +59,12 @@ define('TELEGRAM', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php')); require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; // Initializing the robot -$robot = new telegram(TELEGRAM['constructor']['key']); +$robot = new telegram( + token: TELEGRAM['constructor']['key'], + config: new telegram_settings( + botName: TELEGRAM['constructor']['name'] + ) +); $robot->setWebhook( url: 'https://' . PROJECT_DOMAIN . '/telegram/constructor/webhook.php', @@ -69,61 +74,3 @@ $robot->setWebhook( drop_pending_updates: false, secret_token: 'bebra228' ); - -/* // Initializing the robots manager settings -$settings = [ - 'bots' => [ - 'constructor' => [ - 'token' => TELEGRAM['constructor']['key'], - 'certificate_path' => PROJECT_CERTIFICATE, - 'webhook_url' => 'https://' . PROJECT_DOMAIN . '/telegram/constructor/webhook.php', - 'commands' => [ - start::class, - ] - ], - 'async_requests' => true, - 'base_bot_url' => 'https://' . PROJECT_DOMAIN . '/telegram', - ] -]; - -// Initializing the robots manager -$telegram = new telegram($settings); - -$updates = $telegram->bot('constructor')->setWebhook([ - 'url' => 'https://' . PROJECT_DOMAIN . '/telegram/constructor/webhook.php', - 'certificate' => PROJECT_CERTIFICATE -]); - */ - - -/* // Initializing the updates listener -$robot->onUpdate(function (context $context): void {}); - -// Initializing the robot middlewares -$robot->middleware([middlewares::class, 'account']); -$robot->middleware([middlewares::class, 'language']); -$robot->middleware([middlewares::class, 'localization']); -$robot->middleware([middlewares::class, 'authorizations']); - -// Initializing the robot commands handlers -$robot->onCommand('start', [commands::class, 'start']); - -$robot->onCommand('start telegram voronka', [commands::class, 'start']); -$robot->onCommand('start parser', [commands::class, 'start']); -$robot->onCommand('start calculator', [commands::class, 'start']); - -$robot->onCommand('language', [commands::class, 'language'])->middleware([middlewares::class, 'settings']); -$robot->onCommand('society', [commands::class, 'society']); - -// Initializing the robot settings language buttons handlers -foreach (language::cases() as $language) { - // Iterating over languages - - // Initializing language buttons - $robot->onCbQueryData(["settings_language_$language->name"], fn(context $context) => settings::language($context, $language)); -}; - -$robot->onCbQueryData('project_create', ['process_project_create', 'name']); - -// Starting chat-robot -$robot->run(); */ diff --git a/kodorvan/constructor/system/public/telegram/constructor/webhook.php b/kodorvan/constructor/system/public/telegram/constructor/webhook.php index 2caf4f5..f920a5f 100644 --- a/kodorvan/constructor/system/public/telegram/constructor/webhook.php +++ b/kodorvan/constructor/system/public/telegram/constructor/webhook.php @@ -8,8 +8,10 @@ namespace kodorvan\constructor; use kodorvan\constructor\models\account, kodorvan\constructor\models\telegram\settings, kodorvan\constructor\models\telegram\commands\start as command_start, + kodorvan\constructor\models\telegram\commands\account as command_account, kodorvan\constructor\models\telegram\commands\society as command_society, kodorvan\constructor\models\telegram\commands\language as command_language, + kodorvan\constructor\models\telegram\conversations\project\create as conversation_project_create, kodorvan\constructor\models\telegram\middlewares\account as middleware_account, kodorvan\constructor\models\telegram\middlewares\language as middleware_language, kodorvan\constructor\models\telegram\middlewares\localization as middleware_localization, @@ -24,10 +26,14 @@ use mirzaev\minimal\core, mirzaev\minimal\route; // Framework for Telegram -/* use Telegram\Bot\BotsManager as telegram; */ use SergiX44\Nutgram\Nutgram as telegram, + SergiX44\Nutgram\Configuration as telegram_settings, SergiX44\Nutgram\RunningMode\Webhook as webhook; +// The symphony cache library +use Symfony\Component\Cache\Adapter\FilesystemAdapter as cache_adapter, + Symfony\Component\Cache\Psr16Cache as cache; + // Enabling debugging /* ini_set('error_reporting', E_ALL); ini_set('display_errors', 1); @@ -64,7 +70,13 @@ define('TELEGRAM', require(SETTINGS . DIRECTORY_SEPARATOR . 'telegram.php')); require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; // Initializing the robot -$robot = new telegram(TELEGRAM['constructor']['key']); +$robot = new telegram( + token: TELEGRAM['constructor']['key'], + config: new telegram_settings( + botName: TELEGRAM['constructor']['name'], + cache: new cache(new cache_adapter()) + ) +); $webhook = new webhook(secretToken: 'bebra228'); $webhook->setSafeMode(true); @@ -77,24 +89,28 @@ $robot->middleware(middleware_localization::class); $robot->middleware(middleware_authorizations::class); -// Initializing the robot commands handlers +// Start $robot->registerCommand(command_start::class); - $robot->onCommand('start telegram voronka', command_start::class); $robot->onCommand('start parser', command_start::class); $robot->onCommand('start calculator', command_start::class); -$robot->registerCommand(command_language::class)->middleware(middleware_settings::class); -$robot->registerCommand(command_society::class); +// Account +$robot->registerCommand(command_account::class); -// Initializing the robot settings language buttons handlers +// Language +$robot->registerCommand(command_language::class)->middleware(middleware_settings::class); foreach (language::cases() as $language) { // Iterating over languages - // Initializing language buttons - $robot->onCallbackQueryData("settings_language_$language->name", fn(telegram $robot) => settings::language(robot: $robot, language: $language)); + // Select the language + $robot->onCallbackQueryData('settings_language_$language->name', fn(telegram $robot) => settings::language(robot: $robot, language: $language)); }; -/* $robot->onCbQueryData('project_create', ['process_project_create', 'name']); */ +// Society +$robot->registerCommand(command_society::class); + +// Project: create +$robot->onCallbackQueryData('project_create', conversation_project_create::class); $robot->run(); diff --git a/kodorvan/constructor/system/settings/system.php.sample b/kodorvan/constructor/system/settings/system.php.sample index bc6ecdf..8249c4d 100644 --- a/kodorvan/constructor/system/settings/system.php.sample +++ b/kodorvan/constructor/system/settings/system.php.sample @@ -3,20 +3,27 @@ // The library for languages support use mirzaev\languages\language; +// The library for currencies support +use mirzaev\currencies\currency; + // Initializing dependencies require ROOT . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; define('SERVER_IP_ADDRESS', '5.140.110.25'); +define('REPOSITORY', 'https://git.svoboda.works'); + define('PROJECT_CREATOR', 'kodorvan'); define('PROJECT_NAME', 'constructor'); define('PROJECT_DOMAIN', 'constructor.kodorvan.tech'); define('PROJECT_CERTIFICATE', SETTINGS . DIRECTORY_SEPARATOR . 'certificate' . DIRECTORY_SEPARATOR . 'public.pem'); -define('REPOSITORY', 'https://git.svoboda.works'); +define('PROJECT_MEDIA_URL', 'https://t.me/kodorvan'); +define('PROJECT_OPERATOR_URL', 'https://t.me/kodorvan?direct'); define('PROJECT_REPOSITORY', REPOSITORY . '/' . PROJECT_CREATOR . '/' . PROJECT_NAME); define('PROJECT_REPOSITORY_LANGUAGE_ADD', PROJECT_REPOSITORY . '/src/branch/stable/' . PROJECT_CREATOR . '/' . PROJECT_NAME . '/system/localizations'); /* define('PROJECT_CERTIFICATE', SETTINGS . DIRECTORY_SEPARATOR . 'cert.pem'); */ +define('CURRENCY_DEFAULT', currency::usd); define('LANGUAGE_DEFAULT', language::en); // Initializing default theme for the views templater diff --git a/kodorvan/constructor/system/settings/telegram.php.sample b/kodorvan/constructor/system/settings/telegram.php.sample index 8a73b5f..5b0515b 100644 --- a/kodorvan/constructor/system/settings/telegram.php.sample +++ b/kodorvan/constructor/system/settings/telegram.php.sample @@ -4,6 +4,7 @@ return [ 'constructor' => [ 'identifier' => 0, + 'name' => 'kodorvan', 'domain' => '', 'key' => '', 'database' => [