feat(bot): add bot commands
This commit is contained in:
@@ -11,7 +11,7 @@ use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\Logger\LoggerInterface;
|
||||
use Openguru\OpenCartFramework\Router\Router;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramClientException;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramClientException;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
|
||||
class BotTokenConfigurator
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
namespace Openguru\OpenCartFramework\Cache;
|
||||
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
|
||||
use Openguru\BulkProducts\Modules\Shared\Cache\CacheInterface;
|
||||
|
||||
class DatabaseCache implements CacheInterface
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Openguru\OpenCartFramework\Http;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Support\Utils;
|
||||
|
||||
class Request
|
||||
final class Request
|
||||
{
|
||||
private $query;
|
||||
private $request;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram\Commands;
|
||||
|
||||
class ChatIdCommand extends TelegramCommand
|
||||
{
|
||||
public function handle(array $update): void
|
||||
{
|
||||
$chatId = $update['message']['chat']['id'];
|
||||
|
||||
$message = sprintf('Chat ID: %s', $chatId);
|
||||
|
||||
$this->telegram->sendMessage($chatId, $message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram\Commands;
|
||||
|
||||
use Openguru\OpenCartFramework\Telegram\Contracts\TelegramCommandInterface;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramBotStateManager;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
|
||||
abstract class TelegramCommand implements TelegramCommandInterface
|
||||
{
|
||||
protected TelegramService $telegram;
|
||||
protected TelegramBotStateManager $state;
|
||||
|
||||
public function __construct(TelegramService $telegram, TelegramBotStateManager $stateManager)
|
||||
{
|
||||
$this->telegram = $telegram;
|
||||
$this->state = $stateManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram\Contracts;
|
||||
|
||||
interface TelegramCommandInterface
|
||||
{
|
||||
public function handle(array $update): void;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram\Enums;
|
||||
|
||||
class ChatAction
|
||||
{
|
||||
public const TYPING = 'typing';
|
||||
public const UPLOAD_PHOTO = 'upload_photo';
|
||||
public const RECORD_VIDEO = 'record_video';
|
||||
public const UPLOAD_VIDEO = 'upload_video';
|
||||
public const RECORD_VOICE = 'record_voice';
|
||||
public const UPLOAD_VOICE = 'upload_voice';
|
||||
public const UPLOAD_DOCUMENT = 'upload_document';
|
||||
public const CHOOSE_STICKER = 'choose_sticker';
|
||||
public const FIND_LOCATION = 'find_location';
|
||||
public const RECORD_VIDEO_NOTE = 'record_video_note';
|
||||
public const UPLOAD_VIDEO_NOTE = 'upload_video_note';
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram;
|
||||
namespace Openguru\OpenCartFramework\Telegram\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class TelegramCommandNotFoundException extends Exception
|
||||
{
|
||||
public function __construct($command = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
$message = "Telegram command `$command` not found";
|
||||
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram;
|
||||
namespace Openguru\OpenCartFramework\Telegram\Exceptions;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace Openguru\OpenCartFramework\Telegram;
|
||||
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramInvalidSignatureException;
|
||||
|
||||
class SignatureValidator
|
||||
{
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram;
|
||||
|
||||
use Openguru\OpenCartFramework\Cache\CacheInterface;
|
||||
|
||||
class TelegramBotStateManager
|
||||
{
|
||||
private CacheInterface $cache;
|
||||
|
||||
public function __construct(CacheInterface $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
public function getStateKey(string $userId, string $chatId): string
|
||||
{
|
||||
return md5('tg-state-' . $userId . '-' . $chatId);
|
||||
}
|
||||
|
||||
public function setState(string $handler, string $userId, string $chatId, array $data = []): void
|
||||
{
|
||||
$payload = [
|
||||
'handler' => $handler,
|
||||
'user_id' => $userId,
|
||||
'chat_id' => $chatId,
|
||||
'data' => $data,
|
||||
];
|
||||
|
||||
$this->cache->set($this->getStateKey($userId, $chatId), $payload, 120);
|
||||
}
|
||||
|
||||
public function getState(string $userId, string $chatId): ?array
|
||||
{
|
||||
return $this->cache->get($this->getStateKey($userId, $chatId));
|
||||
}
|
||||
|
||||
public function hasState(string $userId, string $chatId): bool
|
||||
{
|
||||
return ! empty($this->cache->get($this->getStateKey($userId, $chatId)));
|
||||
}
|
||||
|
||||
public function clearState(string $userId, string $chatId): void
|
||||
{
|
||||
$this->cache->delete($this->getStateKey($userId, $chatId));
|
||||
}
|
||||
|
||||
public function getCurrentStateCommandHandler(string $userId, string $chatId): string
|
||||
{
|
||||
$state = $this->getState($userId, $chatId);
|
||||
|
||||
return $state['handler'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Openguru\OpenCartFramework\Telegram;
|
||||
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramCommandNotFoundException;
|
||||
|
||||
class TelegramCommandsRegistry
|
||||
{
|
||||
private array $commands = [];
|
||||
|
||||
public function addCommand(string $command, string $handler, ?string $description = null): void
|
||||
{
|
||||
$this->commands[$command] = [
|
||||
'handler' => $handler,
|
||||
'description' => $description,
|
||||
];
|
||||
}
|
||||
|
||||
public function resolve(string $command): string
|
||||
{
|
||||
if (! array_key_exists($command, $this->commands)) {
|
||||
throw new TelegramCommandNotFoundException($command);
|
||||
}
|
||||
|
||||
return $this->commands[$command]['handler'];
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\Enums\ChatAction;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramClientException;
|
||||
|
||||
class TelegramService
|
||||
{
|
||||
@@ -22,6 +24,7 @@ class TelegramService
|
||||
foreach ($specials as $char) {
|
||||
$text = str_replace($char, '\\' . $char, $text);
|
||||
}
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
@@ -32,23 +35,35 @@ class TelegramService
|
||||
return str_replace(array_keys($variables), $values, $template);
|
||||
}
|
||||
|
||||
public function sendMessage(int $chatId, string $text): void
|
||||
{
|
||||
/**
|
||||
* @throws TelegramClientException
|
||||
* @throws GuzzleException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function sendMessage(
|
||||
int $chatId,
|
||||
string $text,
|
||||
array $replyMarkup = [],
|
||||
string $chatAction = ChatAction::TYPING,
|
||||
string $parseMode = 'MarkdownV2'
|
||||
): void {
|
||||
if (! $this->botToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
$client = $this->createGuzzleClient("https://api.telegram.org/bot{$this->botToken}/");
|
||||
$this->sendChatAction($chatId, $chatAction);
|
||||
|
||||
$query = [
|
||||
$params = [
|
||||
'chat_id' => $chatId,
|
||||
'text' => $text,
|
||||
'parse_mode' => 'MarkdownV2',
|
||||
'parse_mode' => $parseMode,
|
||||
];
|
||||
|
||||
$client->get('sendMessage', [
|
||||
'query' => $query,
|
||||
]);
|
||||
if ($replyMarkup) {
|
||||
$params['reply_markup'] = json_encode($replyMarkup, JSON_THROW_ON_ERROR);
|
||||
}
|
||||
|
||||
$this->exec('sendMessage', $params);
|
||||
}
|
||||
|
||||
private function createGuzzleClient(string $uri): Client
|
||||
@@ -113,4 +128,54 @@ class TelegramService
|
||||
|
||||
return Arr::get($webhookInfo, 'result.url');
|
||||
}
|
||||
|
||||
public function sendChatAction(int $chatId, string $action): void
|
||||
{
|
||||
$this->exec('sendChatAction', [
|
||||
'chat_id' => $chatId,
|
||||
'action' => $action,
|
||||
]);
|
||||
}
|
||||
|
||||
public function escapeTgSpecialCharacters(string $text): string
|
||||
{
|
||||
// Набор спецсимволов для MarkdownV2
|
||||
$specials = '_*[]()~`>#+-=|{}.!';
|
||||
|
||||
$out = '';
|
||||
$len = strlen($text); // работаем побайтно: спецсимволы — ASCII
|
||||
$prevWasBackslash = false;
|
||||
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
$ch = $text[$i];
|
||||
|
||||
if ($prevWasBackslash) {
|
||||
// Предыдущий был "\", этот символ считаем уже экранированным — просто добавим как есть
|
||||
$out .= $ch;
|
||||
$prevWasBackslash = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($ch === '\\') {
|
||||
// Запоминаем, что встретили слеш; пока не знаем, что дальше
|
||||
$out .= '\\';
|
||||
$prevWasBackslash = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Если текущий символ — спецсимвол и он НЕ экранирован (мы уже знаем, что предыдущий не "\")
|
||||
if (strpos($specials, $ch) !== false) {
|
||||
$out .= '\\' . $ch;
|
||||
} else {
|
||||
$out .= $ch;
|
||||
}
|
||||
}
|
||||
|
||||
// Если строка закончилась на одиночный "\" — довэкранируем его
|
||||
if ($prevWasBackslash) {
|
||||
$out .= '\\';
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,13 @@ class ErrorBag
|
||||
$this->errors[$field][] = $message;
|
||||
}
|
||||
|
||||
public function first(): array
|
||||
public function first(): ?array
|
||||
{
|
||||
foreach ($this->errors as $error) {
|
||||
return $error;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function firstOfAll(): array
|
||||
|
||||
@@ -11,6 +11,7 @@ class Validator implements ValidatorInterface
|
||||
private ErrorBag $errors;
|
||||
private array $customMessages;
|
||||
private array $fieldNames;
|
||||
private array $validationRules;
|
||||
|
||||
public function __construct(array $validationRules = [], array $customMessages = [])
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace App\Exceptions;
|
||||
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Response;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramInvalidSignatureException;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramInvalidSignatureException;
|
||||
use Throwable;
|
||||
|
||||
class CustomExceptionHandler implements ExceptionHandlerInterface
|
||||
|
||||
@@ -2,25 +2,90 @@
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Mockery\Exception;
|
||||
use Openguru\OpenCartFramework\Cache\CacheInterface;
|
||||
use Openguru\OpenCartFramework\Container\Container;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Logger\LoggerInterface;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\Contracts\TelegramCommandInterface;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramCommandNotFoundException;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramBotStateManager;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramCommandsRegistry;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
|
||||
class TelegramHandler
|
||||
{
|
||||
private CacheInterface $cache;
|
||||
private TelegramCommandsRegistry $telegramCommandsRegistry;
|
||||
private Container $container;
|
||||
private TelegramBotStateManager $telegramBotStateManager;
|
||||
private LoggerInterface $logger;
|
||||
private TelegramService $telegramService;
|
||||
|
||||
public function __construct(CacheInterface $cache)
|
||||
{
|
||||
public function __construct(
|
||||
CacheInterface $cache,
|
||||
TelegramCommandsRegistry $telegramCommandsRegistry,
|
||||
Container $container,
|
||||
TelegramBotStateManager $telegramBotStateManager,
|
||||
LoggerInterface $logger,
|
||||
TelegramService $telegramService
|
||||
) {
|
||||
$this->cache = $cache;
|
||||
$this->telegramCommandsRegistry = $telegramCommandsRegistry;
|
||||
$this->container = $container;
|
||||
$this->telegramBotStateManager = $telegramBotStateManager;
|
||||
$this->logger = $logger;
|
||||
$this->telegramService = $telegramService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws GuzzleException
|
||||
* @throws \JsonException
|
||||
*/
|
||||
public function webhook(Request $request): JsonResponse
|
||||
{
|
||||
$message = Arr::get($request->json(), 'message', []);
|
||||
$update = $request->json();
|
||||
$userId = $update['message']['from']['id'];
|
||||
$chatId = $update['message']['chat']['id'];
|
||||
|
||||
$this->cache->set('tg_latest_msg', $message, 60);
|
||||
try {
|
||||
$message = Arr::get($update, 'message', []);
|
||||
|
||||
$this->cache->set('tg_latest_msg', $message, 60);
|
||||
|
||||
$text = Arr::get($message, 'text', '');
|
||||
|
||||
// command starts from "/"
|
||||
if (strpos($text, '/') === 0) {
|
||||
$this->telegramBotStateManager->clearState($userId, $chatId);
|
||||
$command = substr($text, 1);
|
||||
$handler = $this->telegramCommandsRegistry->resolve($command);
|
||||
|
||||
/** @var TelegramCommandInterface $concrete */
|
||||
$concrete = $this->container->get($handler);
|
||||
$concrete->handle($update);
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
// Continue state
|
||||
$hasState = $this->telegramBotStateManager->hasState($userId, $chatId);
|
||||
if ($hasState) {
|
||||
$handler = $this->telegramBotStateManager->getCurrentStateCommandHandler($userId, $chatId);
|
||||
/** @var TelegramCommandInterface $concrete */
|
||||
$concrete = $this->container->get($handler);
|
||||
$concrete->handle($update);
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
} catch (TelegramCommandNotFoundException $exception) {
|
||||
$this->telegramService->sendMessage($chatId, 'Неверная команда');
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->logException($exception);
|
||||
}
|
||||
|
||||
return new JsonResponse([]);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
namespace App\ServiceProviders;
|
||||
|
||||
use App\Exceptions\CustomExceptionHandler;
|
||||
use App\Telegram\LinkCommand;
|
||||
use Openguru\OpenCartFramework\Container\ServiceProvider;
|
||||
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||
use Openguru\OpenCartFramework\Telegram\Commands\ChatIdCommand;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramCommandsRegistry;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -13,5 +16,19 @@ class AppServiceProvider extends ServiceProvider
|
||||
$this->container->singleton(ExceptionHandlerInterface::class, function () {
|
||||
return new CustomExceptionHandler();
|
||||
});
|
||||
|
||||
$this->registerTelegramCommands();
|
||||
}
|
||||
|
||||
private function registerTelegramCommands(): void
|
||||
{
|
||||
$this->container->singleton(TelegramCommandsRegistry::class, function () {
|
||||
return new TelegramCommandsRegistry();
|
||||
});
|
||||
|
||||
$registry = $this->container->get(TelegramCommandsRegistry::class);
|
||||
|
||||
$registry->addCommand('id', ChatIdCommand::class, 'Возвращает ChatID текущего чата.');
|
||||
$registry->addCommand('link', LinkCommand::class, 'Генератор Telegram сообщений с кнопкой');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
namespace App\Telegram;
|
||||
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use JsonException;
|
||||
use Openguru\OpenCartFramework\Logger\LoggerInterface;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\Commands\TelegramCommand;
|
||||
use Openguru\OpenCartFramework\Telegram\Enums\ChatAction;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramBotStateManager;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
use Throwable;
|
||||
|
||||
class LinkCommand extends TelegramCommand
|
||||
{
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
TelegramService $telegram,
|
||||
TelegramBotStateManager $stateManager,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
parent::__construct($telegram, $stateManager);
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws GuzzleException
|
||||
* @throws JsonException
|
||||
*/
|
||||
public function handle(array $update): void
|
||||
{
|
||||
try {
|
||||
$userId = $update['message']['from']['id'];
|
||||
$chatId = $update['message']['chat']['id'];
|
||||
|
||||
$state = $this->state->getState($userId, $chatId);
|
||||
|
||||
if (! $state) {
|
||||
$greeting = $this->telegram->escapeTgSpecialCharacters(
|
||||
<<<MARKDOWN
|
||||
Это удобный инструмент, который поможет вам 📎 создать красивое сообщение с кнопкой для открытия вашего 🛒 Telecart магазина.
|
||||
|
||||
📌 Такое сообщение можно закрепить в канале или группе.
|
||||
📤 Переслать клиентам в личные сообщения.
|
||||
🚀 Или использовать повторно, когда нужно поделиться магазином.
|
||||
|
||||
Давайте начнём — отправьте текст, который вы хотите разместить в сообщении 👇
|
||||
MARKDOWN
|
||||
);
|
||||
$this->telegram->sendMessage($chatId, $greeting);
|
||||
|
||||
$this->state->setState(self::class, $userId, $chatId, [
|
||||
'step' => 'message_text',
|
||||
'data' => [
|
||||
'message_text' => '',
|
||||
'btn_text' => '',
|
||||
'btn_link' => '',
|
||||
],
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$step = $state['data']['step'];
|
||||
|
||||
if ($step === 'message_text') {
|
||||
$message = $update['message']['text'];
|
||||
$state['data']['data']['message_text'] = $message;
|
||||
$state['data']['step'] = 'btn_text';
|
||||
$this->state->setState(self::class, $userId, $chatId, $state['data']);
|
||||
|
||||
$text = <<<MARKDOWN
|
||||
🔸 Отлично\!
|
||||
Теперь укажите, какой текст будет на кнопке 👇
|
||||
|
||||
✍️ Напишите короткую, понятную фразу, например:
|
||||
• `Открыть магазин`
|
||||
• `Каталог товаров`
|
||||
• `Начать покупки`
|
||||
MARKDOWN;
|
||||
$this->telegram->sendMessage($chatId, $text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($step === 'btn_text') {
|
||||
$message = $update['message']['text'];
|
||||
$state['data']['data']['btn_text'] = $message;
|
||||
$state['data']['step'] = 'btn_link';
|
||||
$this->state->setState(self::class, $userId, $chatId, $state['data']);
|
||||
|
||||
$template = <<<MARKDOWN
|
||||
🌐 Теперь отправьте *ссылку на Telegram Mini App*\.
|
||||
Ссылка должна начинаться с `https://`
|
||||
|
||||
📎 Инструкция, где взять ссылку:
|
||||
👉 {LINK}
|
||||
MARKDOWN;
|
||||
|
||||
$text = $this->telegram->prepareMessage($template, [
|
||||
'{LINK}' => 'https://telecart-labs.github.io/docs/telegram/telegram/#direct-link',
|
||||
]);
|
||||
$this->telegram->sendMessage($chatId, $text);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($step === 'btn_link') {
|
||||
$message = $update['message']['text'];
|
||||
$state['data']['data']['btn_link'] = $message;
|
||||
$this->state->setState(self::class, $userId, $chatId, $state['data']);
|
||||
|
||||
$messageText = Arr::get($state, 'data.data.message_text', 'Текст сообщения');
|
||||
$btnText = $this->telegram->escapeTgSpecialCharacters(
|
||||
Arr::get($state, 'data.data.btn_text', 'Открыть магазин')
|
||||
);
|
||||
$btnLink = $message;
|
||||
|
||||
$replyMarkup = [
|
||||
'inline_keyboard' => [
|
||||
[
|
||||
[
|
||||
'text' => $btnText,
|
||||
'url' => $btnLink,
|
||||
]
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$this->telegram->sendMessage(
|
||||
$chatId,
|
||||
$this->telegram->escapeTgSpecialCharacters($messageText),
|
||||
$replyMarkup,
|
||||
);
|
||||
}
|
||||
|
||||
$this->state->clearState($userId, $chatId);
|
||||
} catch (ClientException $exception) {
|
||||
$this->telegram->sendMessage($chatId, 'Ошибка: ' . $exception->getResponse()->getBody()->getContents());
|
||||
} catch (Throwable $exception) {
|
||||
$this->logger->logException($exception);
|
||||
$this->telegram->sendMessage($chatId, 'Произошла ошибка');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Telegram;
|
||||
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TelegramServiceTest extends TestCase
|
||||
{
|
||||
private TelegramService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->service = new TelegramService();
|
||||
}
|
||||
|
||||
public function testDoesNotEscapeNormalCharacters(): void
|
||||
{
|
||||
$this->assertEquals('hello world', $this->service->escapeTgSpecialCharacters('hello world'));
|
||||
}
|
||||
|
||||
public function testEscapesSingleSpecialCharacters(): void
|
||||
{
|
||||
$this->assertEquals('\_', $this->service->escapeTgSpecialCharacters('_'));
|
||||
$this->assertEquals('\#', $this->service->escapeTgSpecialCharacters('#'));
|
||||
$this->assertEquals('\+', $this->service->escapeTgSpecialCharacters('+'));
|
||||
$this->assertEquals('\(', $this->service->escapeTgSpecialCharacters('('));
|
||||
$this->assertEquals('\)', $this->service->escapeTgSpecialCharacters(')'));
|
||||
$this->assertEquals('\!', $this->service->escapeTgSpecialCharacters('!'));
|
||||
}
|
||||
|
||||
public function testDoesNotDoubleEscapeAlreadyEscaped(): void
|
||||
{
|
||||
$this->assertEquals('\#', $this->service->escapeTgSpecialCharacters('\#'));
|
||||
$this->assertEquals('\\\\', $this->service->escapeTgSpecialCharacters('\\\\')); // двойной бэкслеш остаётся двойным
|
||||
}
|
||||
|
||||
public function testEscapesInsideText(): void
|
||||
{
|
||||
$this->assertEquals('price is 100\#', $this->service->escapeTgSpecialCharacters('price is 100#'));
|
||||
$this->assertEquals('a\(b\)c', $this->service->escapeTgSpecialCharacters('a(b)c'));
|
||||
$this->assertEquals('a\+b', $this->service->escapeTgSpecialCharacters('a+b'));
|
||||
}
|
||||
|
||||
public function testEscapesBackslashAtEnd(): void
|
||||
{
|
||||
// висячий бэкслеш должен быть продублирован
|
||||
$this->assertEquals('backslash\\\\', $this->service->escapeTgSpecialCharacters('backslash\\'));
|
||||
}
|
||||
|
||||
public function testDoesNotEscapeEscapedSpecialCharacter(): void
|
||||
{
|
||||
// \# должен остаться \#
|
||||
$this->assertEquals('\#tag', $this->service->escapeTgSpecialCharacters('\#tag'));
|
||||
}
|
||||
|
||||
public function testMultipleSpecialCharactersInRow(): void
|
||||
{
|
||||
$this->assertEquals('\#\+\=', $this->service->escapeTgSpecialCharacters('#+='));
|
||||
$this->assertEquals('\#\+\=text', $this->service->escapeTgSpecialCharacters('#+=text'));
|
||||
}
|
||||
|
||||
public function testEmojiAndMultibyteCharactersAreUntouched(): void
|
||||
{
|
||||
$this->assertEquals('Привет 👋', $this->service->escapeTgSpecialCharacters('Привет 👋'));
|
||||
$this->assertEquals('emoji\#', $this->service->escapeTgSpecialCharacters('emoji#'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user