From 79c82c510958e535c587dc8d22d3ea21a2faf3a2 Mon Sep 17 00:00:00 2001 From: Nikita Kiselev Date: Thu, 14 Aug 2025 21:51:09 +0300 Subject: [PATCH] WIP --- .../controller/extension/module/tgshop.php | 31 +++++ .../template/extension/module/tgshop.twig | 32 ++--- .../controller/extension/tgshop/handle.php | 3 +- .../bastion/ApplicationFactory.php | 32 +++++ .../bastion/Handlers/TelegramHandler.php | 111 ++++++++++++++++++ .../oc_telegram_shop/bastion/config.php | 20 ++++ .../oc_telegram_shop/bastion/routes.php | 11 ++ .../upload/oc_telegram_shop/composer.json | 1 + .../framework/Application.php | 14 +++ .../framework/Cache/CacheServiceProvider.php | 12 +- .../Cache/SymfonyFilesystemCache.php | 44 +++++++ .../framework/Router/RouteServiceProvider.php | 5 +- .../Telegram/TelegramClientException.php | 14 +++ .../framework/Telegram/TelegramService.php | 35 ++++++ .../TelegramValidateInitDataMiddleware.php | 1 + .../src/ApplicationFactory.php | 2 + .../src/Handlers/TelegramHandler.php | 27 +++++ .../ServiceProviders/AppServiceProvider.php | 2 - .../upload/oc_telegram_shop/src/config.php | 21 ---- .../upload/oc_telegram_shop/src/routes.php | 3 + 20 files changed, 365 insertions(+), 56 deletions(-) create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ApplicationFactory.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/TelegramHandler.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/config.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/bastion/routes.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/SymfonyFilesystemCache.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramClientException.php create mode 100755 module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramHandler.php diff --git a/module/oc_telegram_shop/upload/admin/controller/extension/module/tgshop.php b/module/oc_telegram_shop/upload/admin/controller/extension/module/tgshop.php index d7b3e68..df0425f 100755 --- a/module/oc_telegram_shop/upload/admin/controller/extension/module/tgshop.php +++ b/module/oc_telegram_shop/upload/admin/controller/extension/module/tgshop.php @@ -1,5 +1,17 @@ response->setOutput($this->load->view('extension/module/tgshop_init', $data)); } + public function handle(): void + { + $app = ApplicationFactory::create([ + 'base_url' => HTTPS_SERVER, + 'public_url' => HTTPS_CATALOG, + 'telegram' => [ + 'bot_token' => $this->config->get('module_tgshop_bot_token'), + 'chat_id' => $this->config->get('module_tgshop_chat_id'), + 'owner_notification_template' => $this->config->get('module_tgshop_owner_notification_template'), + 'customer_notification_template' => $this->config->get('module_tgshop_customer_notification_template'), + ], + 'logs' => [ + 'path' => DIR_LOGS, + ], + ]); + + $app->bootAndHandleRequest(); + } + protected function validate(): bool { if (! $this->user->hasPermission('modify', 'extension/module/tgshop')) { diff --git a/module/oc_telegram_shop/upload/admin/view/template/extension/module/tgshop.twig b/module/oc_telegram_shop/upload/admin/view/template/extension/module/tgshop.twig index 894e118..fadd2b1 100755 --- a/module/oc_telegram_shop/upload/admin/view/template/extension/module/tgshop.twig +++ b/module/oc_telegram_shop/upload/admin/view/template/extension/module/tgshop.twig @@ -167,35 +167,23 @@ return; } - fetch(`https://api.telegram.org/bot${telegramToken}/getUpdates`) - .then(res => res.json()) - .then(data => { - if (!data.ok || !data.result.length) { - alert('Не удалось получить обновления от бота. Убедитесь, что вы написали боту нужное кодовое слово.'); - return; + fetch('/admin/index.php?route=extension/module/tgshop/handle&api_action=getChatId&user_token={{ user_token }}') + .then(async (res) => { + const data = await res.json().catch(() => null); + + if (!res.ok) { + throw new Error(`Ошибка ${res.status}: ${data.message || res.statusText}`); } - // Ищем последнее сообщение с chat_id - const lastMessage = data.result.reverse().find(update => update.message && update.message.chat); - if (!lastMessage) { - alert('Не найдено сообщений с chat_id.'); - return; - } - - if (lastMessage.message.text !== 'opencart_get_chatid') { - alert('Ошибка. Последнее сообщение у бота не содержит правильного кодового слова.'); - return; - } - - const chatId = lastMessage.message.chat.id; - $('#{{ settingKey }}').val(chatId); // подставляем в поле - alert('ChatID успешно получен и подставлен в поле.') + $('#{{ settingKey }}').val(data.data.chat_id); + alert('ChatID успешно получен и подставлен в поле.'); }) .catch(err => { console.error(err); - alert('Ошибка при получении chat_id. Проверьте токен и соединение.'); + alert(err); }); }); + diff --git a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php index 7ba3677..b0f7876 100755 --- a/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php +++ b/module/oc_telegram_shop/upload/catalog/controller/extension/tgshop/handle.php @@ -17,7 +17,7 @@ if (is_readable($sysLibPath . '/oc_telegram_shop.phar')) { } elseif (is_dir("$basePath/oc_telegram_shop")) { require_once "$basePath/oc_telegram_shop/vendor/autoload.php"; } else { - throw new RuntimeException('Unable to locate bulk products directory.'); + throw new RuntimeException('Unable to locate application directory.'); } /** @@ -52,6 +52,7 @@ class Controllerextensiontgshophandle extends Controller 'theme_dark' => $this->config->get('module_tgshop_theme_dark'), 'mainpage_products' => $this->config->get('module_tgshop_mainpage_products'), 'featured_products' => $this->config->get('module_tgshop_featured_products'), + 'base_url' => HTTPS_SERVER, 'ya_metrika_enabled' => ! empty(trim($this->config->get('module_tgshop_yandex_metrika'))), 'telegram' => [ 'bot_token' => $this->config->get('module_tgshop_bot_token'), diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ApplicationFactory.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ApplicationFactory.php new file mode 100755 index 0000000..9e53e5a --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ApplicationFactory.php @@ -0,0 +1,32 @@ +withRoutes(fn () => $routes) + ->withServiceProviders([ + QueryBuilderServiceProvider::class, + RouteServiceProvider::class, + AppServiceProvider::class, + CacheServiceProvider::class, + TelegramServiceProvider::class, + ]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/TelegramHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/TelegramHandler.php new file mode 100755 index 0000000..283ab51 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/Handlers/TelegramHandler.php @@ -0,0 +1,111 @@ +telegramService = $telegramService; + $this->logger = $logger; + $this->router = $router; + $this->settings = $settings; + $this->cache = $cache; + } + + public function getMe(): JsonResponse + { + $data = $this->telegramService->exec('getMe'); + + return new JsonResponse($data); + } + + public function setWebhook(): JsonResponse + { + $publicUrl = $this->settings->get('public_url'); + if (! $publicUrl || $publicUrl === '/' || str_contains($publicUrl, 'localhost')) { + throw new RuntimeException('public_url is required in settings to create webhook.'); + } + + $webHookUrl = trim($publicUrl, '/') . $this->router->url('webhook'); + + try { + $data = $this->telegramService->exec('setWebhook', [ + 'url' => $webHookUrl, + ]); + + return new JsonResponse($data); + } catch (Exception $exception) { + return new JsonResponse(['error' => $exception->getMessage()], 500); + } + } + + public function getWebhookInfo(): JsonResponse + { + try { + $data = $this->telegramService->exec('getWebhookInfo'); + + return new JsonResponse($data); + } catch (Exception $exception) { + return new JsonResponse(['error' => $exception->getMessage()], 500); + } + } + + public function deleteWebhook(): JsonResponse + { + try { + $data = $this->telegramService->exec('deleteWebhook'); + + return new JsonResponse($data); + } catch (Exception $exception) { + return new JsonResponse(['error' => $exception->getMessage()], 500); + } + } + + public function getChatId(): JsonResponse + { + $message = $this->cache->get('tg_latest_msg'); + + if (! $message) { + return new JsonResponse([ + 'message' => 'Сообщение не найдено. Убедитесь что отправили кодовое слово в чат с ботом и повторите через 10 секунд.' + ], Response::HTTP_UNPROCESSABLE_ENTITY); + } + + $chatId = Arr::get($message, 'chat.id'); + + if (! $chatId) { + return new JsonResponse([ + 'message' => 'ChatID не найден. Убедитесь что отправили кодовое слово в чат с ботом и повторите через 10 секунд.' + ], Response::HTTP_UNPROCESSABLE_ENTITY); + } + + return new JsonResponse([ + 'data' => [ + 'chat_id' => $chatId, + ], + ]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/config.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/config.php new file mode 100755 index 0000000..6d1e1c5 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/config.php @@ -0,0 +1,20 @@ + 'UTC', + 'lang' => 'en-gb', + 'language_id' => 1, + 'auth_user_id' => 0, + 'base_url' => '/', + + 'db' => [ + 'host' => 'localhost', + 'database' => 'not_set', + 'username' => 'not_set', + 'password' => 'not_set', + ], + + 'logs' => [ + 'path' => 'not_set', + ], +]; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/routes.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/routes.php new file mode 100755 index 0000000..f04b6be --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/routes.php @@ -0,0 +1,11 @@ + [TelegramHandler::class, 'getMe'], + 'setWebhook' => [TelegramHandler::class, 'setWebhook'], + 'getWebhookInfo' => [TelegramHandler::class, 'getWebhookInfo'], + 'deleteWebhook' => [TelegramHandler::class, 'deleteWebhook'], + 'getChatId' => [TelegramHandler::class, 'getChatId'], +]; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json index 3adcc90..33482d0 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/composer.json @@ -4,6 +4,7 @@ "psr-4": { "Openguru\\OpenCartFramework\\": "framework/", "App\\": "src/", + "Bastion\\": "bastion/", "Tests\\": "tests/" }, "files": [ diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php index 5ecbeb3..f674992 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Application.php @@ -20,6 +20,8 @@ class Application extends Container private static array $events = []; private array $serviceProviders = []; private array $middlewareStack = []; + private array $routes = []; + private LoggerInterface $logger; public function __construct(array $config) @@ -146,4 +148,16 @@ class Application extends Container return $this; } + + public function withRoutes(callable $callback): Application + { + $this->routes = $callback(); + + return $this; + } + + public function getRoutes(): array + { + return $this->routes; + } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheServiceProvider.php index 2a2b1c7..94136d0 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheServiceProvider.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/CacheServiceProvider.php @@ -2,20 +2,14 @@ namespace Openguru\OpenCartFramework\Cache; -use Openguru\OpenCartFramework\Config\Settings; -use Openguru\OpenCartFramework\Container\Container; use Openguru\OpenCartFramework\Container\ServiceProvider; -use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface; class CacheServiceProvider extends ServiceProvider { public function register(): void { - $this->container->singleton(CacheInterface::class, function (Container $container) { - return new DatabaseCache( - $container->get(ConnectionInterface::class), - $container->get(Settings::class)->get('tables.cache'), - ); + $this->container->singleton(CacheInterface::class, function () { + return new SymfonyFilesystemCache('app.cache', 0, DIR_CACHE); }); } -} \ No newline at end of file +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/SymfonyFilesystemCache.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/SymfonyFilesystemCache.php new file mode 100755 index 0000000..44644d9 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Cache/SymfonyFilesystemCache.php @@ -0,0 +1,44 @@ +cache = new FilesystemAdapter($namespace, $defaultLifetime, $cacheDir); + } + + public function get(string $key) + { + $item = $this->cache->getItem($key); + + return $item->isHit() ? $item->get() : null; + } + + public function set(string $key, $value, ?int $ttlSeconds = null): void + { + $item = $this->cache->getItem($key); + $item->set($value); + + if ($ttlSeconds) { + $item->expiresAfter($ttlSeconds); + } + + $this->cache->save($item); + } + + public function delete(string $key): void + { + $this->cache->deleteItem($key); + } + + public function clear(): void + { + $this->cache->clear(); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php index 3a295b0..a61073b 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Router/RouteServiceProvider.php @@ -2,6 +2,7 @@ namespace Openguru\OpenCartFramework\Router; +use Openguru\OpenCartFramework\Application; use Openguru\OpenCartFramework\Container\Container; use Openguru\OpenCartFramework\Container\ServiceProvider; @@ -10,7 +11,9 @@ class RouteServiceProvider extends ServiceProvider public function register(): void { $this->container->singleton(Router::class, function (Container $container) { - return new Router(); + $routes = Application::getInstance()->getRoutes(); + + return new Router($routes); }); } } \ No newline at end of file diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramClientException.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramClientException.php new file mode 100755 index 0000000..8b0a247 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramClientException.php @@ -0,0 +1,14 @@ +botToken) { + throw new TelegramClientException('BotToken is empty'); + } + + try { + $response = $this->client->post($methodName, [ + 'json' => $params, + ]); + + $json = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); + + return $json; + } catch (ClientException $exception) { + $response = $exception->getResponse()->getBody()->getContents(); + $decoded = json_decode($response, true, 512, JSON_THROW_ON_ERROR); + + throw new TelegramClientException( + $decoded['description'] ?? $exception->getMessage(), + $decoded['code'] ?? $exception->getCode(), + ); + } + } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramValidateInitDataMiddleware.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramValidateInitDataMiddleware.php index 541d8c2..3dcb1c9 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramValidateInitDataMiddleware.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramValidateInitDataMiddleware.php @@ -11,6 +11,7 @@ class TelegramValidateInitDataMiddleware private array $excluded = [ 'testTgMessage', 'manifest', + 'webhook', ]; public function __construct(SignatureValidator $signatureValidator) diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php index b94ef4f..ea28137 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ApplicationFactory.php @@ -17,8 +17,10 @@ class ApplicationFactory public static function create(array $config): Application { $defaultConfig = require __DIR__ . '/config.php'; + $routes = require __DIR__ . '/routes.php'; return (new Application(Arr::mergeArraysRecursively($defaultConfig, $config))) + ->withRoutes(fn () => $routes) ->withServiceProviders([ QueryBuilderServiceProvider::class, CacheServiceProvider::class, diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramHandler.php new file mode 100755 index 0000000..8a2d9fc --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramHandler.php @@ -0,0 +1,27 @@ +cache = $cache; + } + + public function webhook(Request $request): JsonResponse + { + $message = Arr::get($request->json(), 'message', []); + + $this->cache->set('tg_latest_msg', $message, 60); + + return new JsonResponse([]); + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php index ac64782..286b790 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/ServiceProviders/AppServiceProvider.php @@ -5,13 +5,11 @@ namespace App\ServiceProviders; use App\Exceptions\CustomExceptionHandler; use Openguru\OpenCartFramework\Container\ServiceProvider; use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface; -use Openguru\OpenCartFramework\Router\Router; class AppServiceProvider extends ServiceProvider { public function register(): void { - $this->container->get(Router::class)->loadRoutesFromFile(__DIR__ . '/../routes.php'); $this->container->singleton(ExceptionHandlerInterface::class, function () { return new CustomExceptionHandler(); }); diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php index fd958fb..1856d1b 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/config.php @@ -2,32 +2,11 @@ return [ 'config_timezone' => 'UTC', - 'lang' => 'en-gb', 'language_id' => 1, 'auth_user_id' => 0, - 'base_url' => 'http://localhost', - 'search_cache_seconds' => 60, - - 'chunk_size' => 100, - - 'retry_count' => 3, - - 'activity_log_retention_limit' => 5000, - - 'tables' => [ - 'selected_products' => 'bp_selected_products', - 'simulation_results' => 'bp_simulation_results', - 'activity_logs' => 'bp_activity_logs', - 'cache' => 'bp_cache', - 'task_progress' => 'bp_task_progress', - 'task_steps' => 'bp_task_steps', - 'search_results' => 'bp_search_results', - 'settings' => 'bp_settings', - ], - 'db' => [ 'host' => 'localhost', 'database' => 'not_set', diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php index 5dd7974..57a1998 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/routes.php @@ -5,6 +5,7 @@ use App\Handlers\CartHandler; use App\Handlers\OrderHandler; use App\Handlers\ProductsHandler; use App\Handlers\SettingsHandler; +use App\Handlers\TelegramHandler; return [ 'products' => [ProductsHandler::class, 'handle'], @@ -19,4 +20,6 @@ return [ 'settings' => [SettingsHandler::class, 'index'], 'manifest' => [SettingsHandler::class, 'manifest'], 'testTgMessage' => [SettingsHandler::class, 'testTgMessage'], + + 'webhook' => [TelegramHandler::class, 'webhook'], ];