mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-21 14:02:10 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef3b76394c | |||
| 2ecb5c7b04 | |||
| 2e7e9d987f | |||
| cf415b70e9 | |||
| a94dfe0df4 | |||
| 03b1f158d9 | |||
| d853faacf7 | |||
| 576f02efbd | |||
| da7a07145b | |||
| 7cdbfc0f47 | |||
| f29344d2ec |
+4659
File diff suppressed because one or more lines are too long
+5
-6
@@ -48,9 +48,9 @@ use RuntimeException;
|
||||
*/
|
||||
class Api
|
||||
{
|
||||
public const string LIBRARY_VERSION = '1.3.0';
|
||||
public const string LIBRARY_VERSION = '1.4.0';
|
||||
|
||||
public const string API_VERSION = '0.0.6';
|
||||
public const string API_VERSION = '1.2.5';
|
||||
|
||||
private const string API_BASE_URL = 'https://platform-api.max.ru';
|
||||
|
||||
@@ -128,13 +128,13 @@ class Api
|
||||
$httpFactory,
|
||||
$httpFactory,
|
||||
self::API_BASE_URL,
|
||||
self::API_VERSION,
|
||||
null,
|
||||
$this->logger,
|
||||
);
|
||||
}
|
||||
|
||||
$this->client = $client;
|
||||
$this->modelFactory = $modelFactory ?? new ModelFactory();
|
||||
$this->modelFactory = $modelFactory ?? new ModelFactory($this->logger);
|
||||
$this->updateDispatcher = new UpdateDispatcher($this);
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ class Api
|
||||
$this->buildNewMessageBody($text, $attachments, $format, $link, $notify),
|
||||
);
|
||||
|
||||
return $this->modelFactory->createMessage($response['message']);
|
||||
return $this->modelFactory->createMessageFromSendResponse($response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -685,7 +685,6 @@ class Api
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return $this->modelFactory->createMessage($response['message']);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,14 @@ enum UpdateType: string
|
||||
case MessageRemoved = 'message_removed';
|
||||
case BotAdded = 'bot_added';
|
||||
case BotRemoved = 'bot_removed';
|
||||
case DialogMuted = 'dialog_muted';
|
||||
case DialogUnmuted = 'dialog_unmuted';
|
||||
case DialogCleared = 'dialog_cleared';
|
||||
case DialogRemoved = 'dialog_removed';
|
||||
case UserAdded = 'user_added';
|
||||
case UserRemoved = 'user_removed';
|
||||
case BotStarted = 'bot_started';
|
||||
case BotStopped = 'bot_stopped';
|
||||
case ChatTitleChanged = 'chat_title_changed';
|
||||
case MessageChatCreated = 'message_chat_created';
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use Throwable;
|
||||
* Provides convenient methods for integrating Max Bot with Laravel applications.
|
||||
* Handles webhook processing, long polling, and event dispatching within Laravel context.
|
||||
*/
|
||||
class MaxBotManager
|
||||
readonly class MaxBotManager
|
||||
{
|
||||
/**
|
||||
* @param Container $container
|
||||
@@ -35,9 +35,9 @@ class MaxBotManager
|
||||
* @param UpdateDispatcher $dispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly Container $container,
|
||||
private readonly Api $api,
|
||||
private readonly UpdateDispatcher $dispatcher,
|
||||
private Container $container,
|
||||
private Api $api,
|
||||
private UpdateDispatcher $dispatcher,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -229,6 +229,58 @@ class MaxBotManager
|
||||
$this->dispatcher->onBotRemoved($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dialog mute handler.
|
||||
*
|
||||
* @param callable|string $handler Can be a closure, callable, or Laravel container binding.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogMuted(callable|string $handler): void
|
||||
{
|
||||
$this->dispatcher->onDialogMuted($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dialog unmute handler.
|
||||
*
|
||||
* @param callable|string $handler Can be a closure, callable, or Laravel container binding.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogUnmuted(callable|string $handler): void
|
||||
{
|
||||
$this->dispatcher->onDialogUnmuted($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dialog cleared handler.
|
||||
*
|
||||
* @param callable|string $handler Can be a closure, callable, or Laravel container binding.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogCleared(callable|string $handler): void
|
||||
{
|
||||
$this->dispatcher->onDialogCleared($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a dialog removed handler.
|
||||
*
|
||||
* @param callable|string $handler Can be a closure, callable, or Laravel container binding.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogRemoved(callable|string $handler): void
|
||||
{
|
||||
$this->dispatcher->onDialogRemoved($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a user added handler.
|
||||
*
|
||||
@@ -268,6 +320,19 @@ class MaxBotManager
|
||||
$this->dispatcher->onBotStarted($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a bot stopped handler.
|
||||
*
|
||||
* @param callable|string $handler Can be a closure, callable, or Laravel container binding.
|
||||
*
|
||||
* @throws BindingResolutionException
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onBotStopped(callable|string $handler): void
|
||||
{
|
||||
$this->dispatcher->onBotStopped($this->resolveHandler($handler));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a chat title changed handler.
|
||||
*
|
||||
|
||||
@@ -76,14 +76,20 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
$guzzle,
|
||||
$httpFactory,
|
||||
$httpFactory,
|
||||
$config->get('maxbot.base_url', 'https://botapi.max.ru'),
|
||||
$config->get('maxbot.api_version', Api::API_VERSION),
|
||||
$config->get('maxbot.base_url', 'https://platform-api.max.ru'),
|
||||
$config->get('maxbot.api_version'),
|
||||
$logger,
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->singleton(ModelFactory::class, function () {
|
||||
return new ModelFactory();
|
||||
$this->app->singleton(ModelFactory::class, function (Application $app) {
|
||||
/** @var Config $config */
|
||||
$config = $app->make(Config::class);
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new ModelFactory($logger);
|
||||
});
|
||||
|
||||
$this->app->singleton(Api::class, function (Application $app) {
|
||||
@@ -97,11 +103,15 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
);
|
||||
}
|
||||
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new Api(
|
||||
$accessToken,
|
||||
$app->make(ClientApiInterface::class),
|
||||
$app->make(ModelFactory::class),
|
||||
$app->make(LoggerInterface::class),
|
||||
$logger,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -114,19 +124,30 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
$config = $app->make(Config::class);
|
||||
$secret = $config->get('maxbot.webhook_secret');
|
||||
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new WebhookHandler(
|
||||
$app->make(UpdateDispatcher::class),
|
||||
$app->make(ModelFactory::class),
|
||||
$app->make(LoggerInterface::class),
|
||||
$logger,
|
||||
$secret,
|
||||
);
|
||||
});
|
||||
|
||||
$this->app->bind(LongPollingHandler::class, function (Application $app) {
|
||||
/** @var Config $config */
|
||||
$config = $app->make(Config::class);
|
||||
|
||||
$logger = $config->get('maxbot.logging.enabled', false)
|
||||
? $app->make(LoggerInterface::class)
|
||||
: new NullLogger();
|
||||
|
||||
return new LongPollingHandler(
|
||||
$app->make(Api::class),
|
||||
$app->make(UpdateDispatcher::class),
|
||||
$app->make(LoggerInterface::class),
|
||||
$logger,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+58
-3
@@ -56,7 +56,12 @@ use BushlanovDev\MaxMessengerBot\Models\Updates\AbstractUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\BotAddedToChatUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\BotRemovedFromChatUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\BotStartedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\BotStoppedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\ChatTitleChangedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogClearedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogMutedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogRemovedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogUnmutedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\MessageCallbackUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\MessageChatCreatedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\MessageCreatedUpdate;
|
||||
@@ -67,13 +72,25 @@ use BushlanovDev\MaxMessengerBot\Models\Updates\UserRemovedFromChatUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\UploadEndpoint;
|
||||
use BushlanovDev\MaxMessengerBot\Models\VideoAttachmentDetails;
|
||||
use LogicException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use ReflectionException;
|
||||
|
||||
/**
|
||||
* Creates DTOs from raw associative arrays returned by the API client.
|
||||
*/
|
||||
class ModelFactory
|
||||
readonly class ModelFactory
|
||||
{
|
||||
private LoggerInterface $logger;
|
||||
|
||||
/**
|
||||
* @param LoggerInterface|null $logger PSR LoggerInterface.
|
||||
*/
|
||||
public function __construct(?LoggerInterface $logger = null)
|
||||
{
|
||||
$this->logger = $logger ?? new NullLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple response to request.
|
||||
*
|
||||
@@ -128,6 +145,33 @@ class ModelFactory
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Message from the specific response structure of the sendMessage endpoint.
|
||||
*
|
||||
* @param array<string, mixed> $data The raw response from the client.
|
||||
*
|
||||
* @return Message
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
public function createMessageFromSendResponse(array $data): Message
|
||||
{
|
||||
$messageData = $data['message'];
|
||||
|
||||
$topLevelData = [
|
||||
'chat_id' => $data['chat_id'] ?? null,
|
||||
'recipient_id' => $data['recipient_id'] ?? null,
|
||||
'message_id' => $data['message_id'] ?? null,
|
||||
];
|
||||
$messageData = array_merge($messageData, array_filter($topLevelData, fn($value) => $value !== null));
|
||||
|
||||
if (isset($messageData['message']) && is_array($messageData['message'])) {
|
||||
$messageData['body'] = $messageData['message'];
|
||||
unset($messageData['message']);
|
||||
}
|
||||
|
||||
return $this->createMessage($messageData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Message.
|
||||
*
|
||||
@@ -267,7 +311,9 @@ class ModelFactory
|
||||
InlineButtonType::RequestGeoLocation => RequestGeoLocationButton::fromArray($data),
|
||||
InlineButtonType::Chat => ChatButton::fromArray($data),
|
||||
InlineButtonType::OpenApp => OpenAppButton::fromArray($data),
|
||||
default => throw new LogicException('Unknown or unsupported inline button type: ' . ($data['type'] ?? 'none')),
|
||||
default => throw new LogicException(
|
||||
'Unknown or unsupported inline button type: ' . ($data['type'] ?? 'none')
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -312,7 +358,11 @@ class ModelFactory
|
||||
if (isset($data['updates']) && is_array($data['updates'])) {
|
||||
foreach ($data['updates'] as $updateData) {
|
||||
// Here we delegate the creation of a specific update to another factory method
|
||||
$updateObjects[] = $this->createUpdate($updateData);
|
||||
try {
|
||||
$updateObjects[] = $this->createUpdate($updateData);
|
||||
} catch (LogicException $e) {
|
||||
$this->logger->debug($e->getMessage(), ['payload' => $updateData, 'exception' => $e]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,9 +390,14 @@ class ModelFactory
|
||||
UpdateType::MessageRemoved => MessageRemovedUpdate::fromArray($data),
|
||||
UpdateType::BotAdded => BotAddedToChatUpdate::fromArray($data),
|
||||
UpdateType::BotRemoved => BotRemovedFromChatUpdate::fromArray($data),
|
||||
UpdateType::DialogMuted => DialogMutedUpdate::fromArray($data),
|
||||
UpdateType::DialogUnmuted => DialogUnmutedUpdate::fromArray($data),
|
||||
UpdateType::DialogCleared => DialogClearedUpdate::fromArray($data),
|
||||
UpdateType::DialogRemoved => DialogRemovedUpdate::fromArray($data),
|
||||
UpdateType::UserAdded => UserAddedToChatUpdate::fromArray($data),
|
||||
UpdateType::UserRemoved => UserRemovedFromChatUpdate::fromArray($data),
|
||||
UpdateType::BotStarted => BotStartedUpdate::fromArray($data),
|
||||
UpdateType::BotStopped => BotStoppedUpdate::fromArray($data),
|
||||
UpdateType::ChatTitleChanged => ChatTitleChangedUpdate::fromArray($data),
|
||||
UpdateType::MessageChatCreated => MessageChatCreatedUpdate::fromArray($data),
|
||||
default => throw new LogicException(
|
||||
|
||||
@@ -17,6 +17,9 @@ final readonly class Message extends AbstractModel
|
||||
* @param string|null $url Message public URL. Can be null for dialogs or non-public chats/channels.
|
||||
* @param LinkedMessage|null $link Forwarded or replied message.
|
||||
* @param MessageStat|null $stat Message statistics. Available only for channels.
|
||||
* @param int|null $chatId Chat identifier.
|
||||
* @param int|null $recipientId User identifier, if message was sent to user.
|
||||
* @param string|null $messageId Unique identifier of message.
|
||||
*/
|
||||
public function __construct(
|
||||
public int $timestamp,
|
||||
@@ -26,6 +29,9 @@ final readonly class Message extends AbstractModel
|
||||
public ?string $url,
|
||||
public ?LinkedMessage $link,
|
||||
public ?MessageStat $stat,
|
||||
public ?int $chatId = null,
|
||||
public ?int $recipientId = null,
|
||||
public ?string $messageId = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
|
||||
/**
|
||||
* The bot receives this type of update as soon as the user stops the bot.
|
||||
*/
|
||||
final readonly class BotStoppedUpdate extends AbstractUpdate
|
||||
{
|
||||
/**
|
||||
* @param int $timestamp Unix-time when event has occurred.
|
||||
* @param int $chatId Dialog identifier where event has occurred.
|
||||
* @param User $user User pressed the 'Start' button.
|
||||
* @param string|null $userLocale Current user locale in IETF BCP 47 format.
|
||||
*/
|
||||
public function __construct(
|
||||
int $timestamp,
|
||||
public int $chatId,
|
||||
public User $user,
|
||||
public ?string $userLocale,
|
||||
) {
|
||||
parent::__construct(UpdateType::BotStopped, $timestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
|
||||
/**
|
||||
* Event clearing dialog history.
|
||||
*/
|
||||
final readonly class DialogClearedUpdate extends AbstractUpdate
|
||||
{
|
||||
/**
|
||||
* @param int $timestamp Unix-time when event has occurred.
|
||||
* @param int $chatId Dialog identifier where event has occurred.
|
||||
* @param User $user User pressed the 'Start' button.
|
||||
* @param string|null $userLocale Current user locale in IETF BCP 47 format.
|
||||
*/
|
||||
public function __construct(
|
||||
int $timestamp,
|
||||
public int $chatId,
|
||||
public User $user,
|
||||
public ?string $userLocale,
|
||||
) {
|
||||
parent::__construct(UpdateType::DialogCleared, $timestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
|
||||
/**
|
||||
* Event when a user mutes a conversation with a bot.
|
||||
*/
|
||||
final readonly class DialogMutedUpdate extends AbstractUpdate
|
||||
{
|
||||
/**
|
||||
* @param int $timestamp Unix-time when event has occurred.
|
||||
* @param int $chatId Dialog identifier where event has occurred.
|
||||
* @param User $user User pressed the 'Start' button.
|
||||
* @param int|null $mutedUntil The time in Unix format before which the dialog was disabled.
|
||||
* @param string|null $userLocale Current user locale in IETF BCP 47 format.
|
||||
*/
|
||||
public function __construct(
|
||||
int $timestamp,
|
||||
public int $chatId,
|
||||
public User $user,
|
||||
public ?int $mutedUntil,
|
||||
public ?string $userLocale,
|
||||
) {
|
||||
parent::__construct(UpdateType::DialogMuted, $timestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
|
||||
/**
|
||||
* Event deleting a chat.
|
||||
*/
|
||||
final readonly class DialogRemovedUpdate extends AbstractUpdate
|
||||
{
|
||||
/**
|
||||
* @param int $timestamp Unix-time when event has occurred.
|
||||
* @param int $chatId Dialog identifier where event has occurred.
|
||||
* @param User $user User pressed the 'Start' button.
|
||||
* @param string|null $userLocale Current user locale in IETF BCP 47 format.
|
||||
*/
|
||||
public function __construct(
|
||||
int $timestamp,
|
||||
public int $chatId,
|
||||
public User $user,
|
||||
public ?string $userLocale,
|
||||
) {
|
||||
parent::__construct(UpdateType::DialogRemoved, $timestamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
|
||||
/**
|
||||
* Event of enabling notifications in a dialog.
|
||||
*/
|
||||
final readonly class DialogUnmutedUpdate extends AbstractUpdate
|
||||
{
|
||||
/**
|
||||
* @param int $timestamp Unix-time when event has occurred.
|
||||
* @param int $chatId Dialog identifier where event has occurred.
|
||||
* @param User $user User pressed the 'Start' button.
|
||||
* @param string|null $userLocale Current user locale in IETF BCP 47 format.
|
||||
*/
|
||||
public function __construct(
|
||||
int $timestamp,
|
||||
public int $chatId,
|
||||
public User $user,
|
||||
public ?string $userLocale,
|
||||
) {
|
||||
parent::__construct(UpdateType::DialogUnmuted, $timestamp);
|
||||
}
|
||||
}
|
||||
@@ -164,6 +164,58 @@ final class UpdateDispatcher
|
||||
return $this->addHandler(UpdateType::BotRemoved, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::DialogMuted, $handler).
|
||||
*
|
||||
* @param callable(Models\Updates\DialogMutedUpdate, Api): void $handler
|
||||
*
|
||||
* @return $this
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogMuted(callable $handler): self
|
||||
{
|
||||
return $this->addHandler(UpdateType::DialogMuted, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::DialogUnmuted, $handler).
|
||||
*
|
||||
* @param callable(Models\Updates\DialogUnmutedUpdate, Api): void $handler
|
||||
*
|
||||
* @return $this
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogUnmuted(callable $handler): self
|
||||
{
|
||||
return $this->addHandler(UpdateType::DialogUnmuted, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::DialogCleared, $handler).
|
||||
*
|
||||
* @param callable(Models\Updates\DialogClearedUpdate, Api): void $handler
|
||||
*
|
||||
* @return $this
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogCleared(callable $handler): self
|
||||
{
|
||||
return $this->addHandler(UpdateType::DialogCleared, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::DialogRemoved, $handler).
|
||||
*
|
||||
* @param callable(Models\Updates\DialogRemovedUpdate, Api): void $handler
|
||||
*
|
||||
* @return $this
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onDialogRemoved(callable $handler): self
|
||||
{
|
||||
return $this->addHandler(UpdateType::DialogRemoved, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::UserAdded, $handler).
|
||||
*
|
||||
@@ -203,6 +255,19 @@ final class UpdateDispatcher
|
||||
return $this->addHandler(UpdateType::BotStarted, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::BotStopped, $handler).
|
||||
*
|
||||
* @param callable(Models\Updates\BotStoppedUpdate, Api): void $handler
|
||||
*
|
||||
* @return $this
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function onBotStopped(callable $handler): self
|
||||
{
|
||||
return $this->addHandler(UpdateType::BotStopped, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenient alias for addHandler(UpdateType::ChatTitleChanged, $handler).
|
||||
*
|
||||
|
||||
@@ -69,9 +69,12 @@ final readonly class WebhookHandler
|
||||
throw new SerializationException('Failed to decode webhook body as JSON.', 0, $e);
|
||||
}
|
||||
|
||||
$update = $this->modelFactory->createUpdate($data);
|
||||
|
||||
$this->dispatcher->dispatch($update);
|
||||
try {
|
||||
$update = $this->modelFactory->createUpdate($data);
|
||||
$this->dispatcher->dispatch($update);
|
||||
} catch (\LogicException $e) {
|
||||
$this->logger->debug($e->getMessage(), ['payload' => $payload, 'exception' => $e]);
|
||||
}
|
||||
|
||||
if (!headers_sent()) {
|
||||
http_response_code(200);
|
||||
|
||||
+53
-25
@@ -118,6 +118,7 @@ use RuntimeException;
|
||||
#[UsesClass(VideoAttachmentDetails::class)]
|
||||
#[UsesClass(VideoUrls::class)]
|
||||
#[UsesClass(UpdateDispatcher::class)]
|
||||
#[UsesClass(ModelFactory::class)]
|
||||
final class ApiTest extends TestCase
|
||||
{
|
||||
use PHPMock;
|
||||
@@ -336,7 +337,7 @@ final class ApiTest extends TestCase
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'timestamp' => time(),
|
||||
'body' => ['mid' => 'mid.456.xyz', 'seq' => 101, 'text' => $text],
|
||||
'message' => ['mid' => 'mid.456.xyz', 'seq' => 101, 'text' => $text],
|
||||
'recipient' => ['chat_type' => 'dialog', 'user_id' => 123, 'chat_id' => null],
|
||||
'sender' => [
|
||||
'user_id' => 123,
|
||||
@@ -348,9 +349,18 @@ final class ApiTest extends TestCase
|
||||
],
|
||||
'url' => 'https://max.ru/message/123',
|
||||
],
|
||||
'chat_id' => 20414985,
|
||||
'recipient_id' => 4328369,
|
||||
'message_id' => 'mid.456.xyz',
|
||||
];
|
||||
|
||||
$expectedMessageObject = Message::fromArray($apiResponse['message']);
|
||||
$finalMessageData = $apiResponse['message'];
|
||||
$finalMessageData['body'] = $finalMessageData['message'];
|
||||
unset($finalMessageData['message']);
|
||||
$finalMessageData['chat_id'] = $apiResponse['chat_id'];
|
||||
$finalMessageData['recipient_id'] = $apiResponse['recipient_id'];
|
||||
$finalMessageData['message_id'] = $apiResponse['message_id'];
|
||||
$expectedMessageObject = Message::fromArray($finalMessageData);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
@@ -360,8 +370,8 @@ final class ApiTest extends TestCase
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($apiResponse['message'])
|
||||
->method('createMessageFromSendResponse')
|
||||
->with($apiResponse)
|
||||
->willReturn($expectedMessageObject);
|
||||
|
||||
$result = $this->api->sendMessage(
|
||||
@@ -416,12 +426,17 @@ final class ApiTest extends TestCase
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'timestamp' => time(),
|
||||
'body' => ['mid' => 'mid.test.123', 'seq' => 1, 'text' => $text],
|
||||
'message' => ['mid' => 'mid.test.123', 'seq' => 1, 'text' => $text],
|
||||
'recipient' => ['chat_type' => 'dialog', 'user_id' => 123, 'chat_id' => null],
|
||||
]
|
||||
],
|
||||
'message_id' => 'mid.test.123',
|
||||
];
|
||||
|
||||
$expectedMessageObject = Message::fromArray($apiResponse['message']);
|
||||
$finalMessageData = $apiResponse['message'];
|
||||
$finalMessageData['body'] = $finalMessageData['message'];
|
||||
unset($finalMessageData['message']);
|
||||
$finalMessageData['message_id'] = $apiResponse['message_id'];
|
||||
$expectedMessageObject = Message::fromArray($finalMessageData);
|
||||
|
||||
$this->clientMock
|
||||
->expects($this->once())
|
||||
@@ -436,8 +451,8 @@ final class ApiTest extends TestCase
|
||||
|
||||
$this->modelFactoryMock
|
||||
->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($apiResponse['message'])
|
||||
->method('createMessageFromSendResponse')
|
||||
->with($apiResponse)
|
||||
->willReturn($expectedMessageObject);
|
||||
|
||||
$result = $this->api->sendMessage(
|
||||
@@ -803,11 +818,15 @@ final class ApiTest extends TestCase
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'timestamp' => time(),
|
||||
'body' => ['mid' => 'mid.sticker.1', 'seq' => 10],
|
||||
'message' => ['mid' => 'mid.sticker.1', 'seq' => 10],
|
||||
'recipient' => ['chat_type' => 'dialog', 'user_id' => $chatId],
|
||||
]
|
||||
];
|
||||
$expectedMessageObject = Message::fromArray($apiResponse['message']);
|
||||
|
||||
$finalMessageData = $apiResponse['message'];
|
||||
$finalMessageData['body'] = $finalMessageData['message'];
|
||||
unset($finalMessageData['message']);
|
||||
$expectedMessageObject = Message::fromArray($finalMessageData);
|
||||
|
||||
$this->clientMock->expects($this->once())
|
||||
->method('request')
|
||||
@@ -815,8 +834,8 @@ final class ApiTest extends TestCase
|
||||
->willReturn($apiResponse);
|
||||
|
||||
$this->modelFactoryMock->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($apiResponse['message'])
|
||||
->method('createMessageFromSendResponse')
|
||||
->with($apiResponse)
|
||||
->willReturn($expectedMessageObject);
|
||||
|
||||
$result = $this->api->sendMessage(chatId: $chatId, attachments: [$stickerRequest]);
|
||||
@@ -849,11 +868,14 @@ final class ApiTest extends TestCase
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'timestamp' => time(),
|
||||
'body' => ['mid' => 'mid.contact.1', 'seq' => 11],
|
||||
'message' => ['mid' => 'mid.contact.1', 'seq' => 11],
|
||||
'recipient' => ['chat_type' => 'dialog', 'user_id' => $chatId],
|
||||
]
|
||||
];
|
||||
$expectedMessageObject = Message::fromArray($apiResponse['message']);
|
||||
$finalMessageData = $apiResponse['message'];
|
||||
$finalMessageData['body'] = $finalMessageData['message'];
|
||||
unset($finalMessageData['message']);
|
||||
$expectedMessageObject = Message::fromArray($finalMessageData);
|
||||
|
||||
$this->clientMock->expects($this->once())
|
||||
->method('request')
|
||||
@@ -861,8 +883,8 @@ final class ApiTest extends TestCase
|
||||
->willReturn($apiResponse);
|
||||
|
||||
$this->modelFactoryMock->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($apiResponse['message'])
|
||||
->method('createMessageFromSendResponse')
|
||||
->with($apiResponse)
|
||||
->willReturn($expectedMessageObject);
|
||||
|
||||
$result = $this->api->sendMessage(chatId: $chatId, attachments: [$contactRequest]);
|
||||
@@ -895,11 +917,14 @@ final class ApiTest extends TestCase
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'timestamp' => time(),
|
||||
'body' => ['mid' => 'mid.location.1', 'seq' => 12],
|
||||
'message' => ['mid' => 'mid.location.1', 'seq' => 12],
|
||||
'recipient' => ['chat_type' => 'dialog', 'user_id' => $chatId],
|
||||
]
|
||||
];
|
||||
$expectedMessageObject = Message::fromArray($apiResponse['message']);
|
||||
$finalMessageData = $apiResponse['message'];
|
||||
$finalMessageData['body'] = $finalMessageData['message'];
|
||||
unset($finalMessageData['message']);
|
||||
$expectedMessageObject = Message::fromArray($finalMessageData);
|
||||
|
||||
$this->clientMock->expects($this->once())
|
||||
->method('request')
|
||||
@@ -907,8 +932,8 @@ final class ApiTest extends TestCase
|
||||
->willReturn($apiResponse);
|
||||
|
||||
$this->modelFactoryMock->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($apiResponse['message'])
|
||||
->method('createMessageFromSendResponse')
|
||||
->with($apiResponse)
|
||||
->willReturn($expectedMessageObject);
|
||||
|
||||
$result = $this->api->sendMessage(chatId: $chatId, attachments: [$locationRequest]);
|
||||
@@ -940,11 +965,14 @@ final class ApiTest extends TestCase
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'timestamp' => time(),
|
||||
'body' => ['mid' => 'mid.share.1', 'seq' => 13],
|
||||
'message' => ['mid' => 'mid.share.1', 'seq' => 13],
|
||||
'recipient' => ['chat_type' => 'dialog', 'user_id' => $chatId],
|
||||
]
|
||||
];
|
||||
$expectedMessageObject = Message::fromArray($apiResponse['message']);
|
||||
$finalMessageData = $apiResponse['message'];
|
||||
$finalMessageData['body'] = $finalMessageData['message'];
|
||||
unset($finalMessageData['message']);
|
||||
$expectedMessageObject = Message::fromArray($finalMessageData);
|
||||
|
||||
$this->clientMock->expects($this->once())
|
||||
->method('request')
|
||||
@@ -952,8 +980,8 @@ final class ApiTest extends TestCase
|
||||
->willReturn($apiResponse);
|
||||
|
||||
$this->modelFactoryMock->expects($this->once())
|
||||
->method('createMessage')
|
||||
->with($apiResponse['message'])
|
||||
->method('createMessageFromSendResponse')
|
||||
->with($apiResponse)
|
||||
->willReturn($expectedMessageObject);
|
||||
|
||||
$result = $this->api->sendMessage(chatId: $chatId, attachments: [$shareRequest]);
|
||||
|
||||
@@ -38,6 +38,7 @@ use ReflectionClass;
|
||||
#[UsesClass(UpdateDispatcher::class)]
|
||||
#[UsesClass(MaxBotManager::class)]
|
||||
#[UsesClass(WebhookHandler::class)]
|
||||
#[UsesClass(ModelFactory::class)]
|
||||
final class MaxBotServiceProviderTest extends TestCase
|
||||
{
|
||||
use PHPMock;
|
||||
@@ -212,6 +213,105 @@ final class MaxBotServiceProviderTest extends TestCase
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function modelFactoryIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var ModelFactory $factory */
|
||||
$factory = $this->app->make(ModelFactory::class);
|
||||
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($factory);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function modelFactoryIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', false);
|
||||
|
||||
/** @var ModelFactory $factory */
|
||||
$factory = $this->app->make(ModelFactory::class);
|
||||
|
||||
$reflection = new ReflectionClass($factory);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($factory);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhookHandlerIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var WebhookHandler $handler */
|
||||
$handler = $this->app->make(WebhookHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhookHandlerIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', false);
|
||||
|
||||
/** @var WebhookHandler $handler */
|
||||
$handler = $this->app->make(WebhookHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function longPollingHandlerIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var LongPollingHandler $handler */
|
||||
$handler = $this->app->make(LongPollingHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function longPollingHandlerIsConfiguredWithNullLoggerWhenLoggingIsDisabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', false);
|
||||
|
||||
/** @var LongPollingHandler $handler */
|
||||
$handler = $this->app->make(LongPollingHandler::class);
|
||||
|
||||
$reflection = new ReflectionClass($handler);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($handler);
|
||||
|
||||
$this->assertInstanceOf(NullLogger::class, $actualLogger);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function webhookHandlerIsConfiguredWithSecretFromConfig(): void
|
||||
{
|
||||
@@ -240,10 +340,28 @@ final class MaxBotServiceProviderTest extends TestCase
|
||||
|
||||
$this->assertSame($this->app->make(ClientApiInterface::class), $clientProp->getValue($api));
|
||||
$this->assertSame($this->app->make(ModelFactory::class), $factoryProp->getValue($api));
|
||||
$this->assertSame($this->app->make(LoggerInterface::class), $loggerProp->getValue($api));
|
||||
$this->assertInstanceOf(NullLogger::class, $loggerProp->getValue($api));
|
||||
$this->assertSame($this->app->make(UpdateDispatcher::class), $dispatcherProp->getValue($api));
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function apiIsConfiguredWithApplicationLoggerWhenLoggingIsEnabled(): void
|
||||
{
|
||||
$this->app['config']->set('maxbot.logging.enabled', true);
|
||||
|
||||
$mockLogger = $this->createMock(LoggerInterface::class);
|
||||
$this->app->instance(LoggerInterface::class, $mockLogger);
|
||||
|
||||
/** @var Api $api */
|
||||
$api = $this->app->make(Api::class);
|
||||
|
||||
$reflection = new ReflectionClass($api);
|
||||
$loggerProp = $reflection->getProperty('logger');
|
||||
$actualLogger = $loggerProp->getValue($api);
|
||||
|
||||
$this->assertSame($mockLogger, $actualLogger);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, array{0: string}>
|
||||
*/
|
||||
|
||||
@@ -59,6 +59,7 @@ use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
#[CoversClass(ModelFactory::class)]
|
||||
#[UsesClass(BotInfo::class)]
|
||||
@@ -220,6 +221,36 @@ final class ModelFactoryTest extends TestCase
|
||||
$this->assertSame(UpdateType::MessageCreated, $subscriptions[0]->updateTypes[0]);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createMessageFromSendResponseCorrectlyTransformsData(): void
|
||||
{
|
||||
$apiResponse = [
|
||||
'message' => [
|
||||
'recipient' => ['chat_id' => 20414985, 'chat_type' => 'dialog', 'user_id' => 4328369],
|
||||
'timestamp' => 1760089962345,
|
||||
'sender' => [
|
||||
'user_id' => 24480184,
|
||||
'first_name' => 'Autobot',
|
||||
'is_bot' => true,
|
||||
'last_activity_time' => 1760089962354,
|
||||
],
|
||||
'message' => ['mid' => 'mid.xyz', 'seq' => 1153, 'text' => '123123'],
|
||||
],
|
||||
'chat_id' => 20414985,
|
||||
'recipient_id' => 4328369,
|
||||
'message_id' => 'mid.xyz',
|
||||
];
|
||||
|
||||
$message = $this->factory->createMessageFromSendResponse($apiResponse);
|
||||
|
||||
$this->assertInstanceOf(Message::class, $message);
|
||||
$this->assertInstanceOf(MessageBody::class, $message->body);
|
||||
$this->assertSame('mid.xyz', $message->body->mid);
|
||||
$this->assertSame(20414985, $message->chatId);
|
||||
$this->assertSame(4328369, $message->recipientId);
|
||||
$this->assertSame('mid.xyz', $message->messageId);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createMessage(): void
|
||||
{
|
||||
@@ -788,4 +819,55 @@ final class ModelFactoryTest extends TestCase
|
||||
|
||||
$assertionCallback($this, $attachment);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function createUpdateListCatchesAndLogsLogicException(): void
|
||||
{
|
||||
$loggerMock = $this->createMock(LoggerInterface::class);
|
||||
$factory = $this->getMockBuilder(ModelFactory::class)
|
||||
->setConstructorArgs([$loggerMock])
|
||||
->onlyMethods(['createUpdate'])
|
||||
->getMock();
|
||||
|
||||
$validUpdateData = [
|
||||
'update_type' => 'bot_started',
|
||||
'timestamp' => 2,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 2,
|
||||
],
|
||||
'payload' => 'start_payload',
|
||||
'user_locale' => 'ru-RU',
|
||||
];
|
||||
$invalidUpdateData = ['update_type' => 'unknown_type'];
|
||||
$rawData = [
|
||||
'updates' => [$validUpdateData, $invalidUpdateData],
|
||||
'marker' => 123,
|
||||
];
|
||||
|
||||
$exception = new LogicException('Unknown or unsupported update type received: unknown_type');
|
||||
$factory->expects($this->exactly(2))
|
||||
->method('createUpdate')
|
||||
->willReturnCallback(function ($data) use ($validUpdateData, $invalidUpdateData, $exception) {
|
||||
if ($data === $validUpdateData) {
|
||||
return BotStartedUpdate::fromArray($data);
|
||||
}
|
||||
if ($data === $invalidUpdateData) {
|
||||
throw $exception;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
$loggerMock->expects($this->once())
|
||||
->method('debug')
|
||||
->with($exception->getMessage(), ['payload' => $invalidUpdateData, 'exception' => $exception]);
|
||||
|
||||
$updateList = $factory->createUpdateList($rawData);
|
||||
|
||||
$this->assertCount(1, $updateList->updates);
|
||||
$this->assertInstanceOf(BotStartedUpdate::class, $updateList->updates[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ final class MessageTest extends TestCase
|
||||
'stat' => [
|
||||
'views' => 500,
|
||||
],
|
||||
'chat_id' => null,
|
||||
'recipient_id' => null,
|
||||
'message_id' => null,
|
||||
];
|
||||
|
||||
$message = Message::fromArray($data);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\BotStoppedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(BotStoppedUpdate::class)]
|
||||
#[UsesClass(User::class)]
|
||||
final class BotStoppedUpdateTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function canBeCreatedFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'update_type' => UpdateType::BotStopped->value,
|
||||
'timestamp' => 1678886400000,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1678886400000,
|
||||
],
|
||||
'user_locale' => 'ru-ru',
|
||||
];
|
||||
|
||||
$update = BotStoppedUpdate::fromArray($data);
|
||||
|
||||
$this->assertInstanceOf(BotStoppedUpdate::class, $update);
|
||||
$this->assertSame(UpdateType::BotStopped, $update->updateType);
|
||||
$this->assertSame(123, $update->user->userId);
|
||||
$this->assertSame('John', $update->user->firstName);
|
||||
$this->assertSame('Doe', $update->user->lastName);
|
||||
$this->assertSame('ru-ru', $update->userLocale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogClearedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(DialogClearedUpdate::class)]
|
||||
#[UsesClass(User::class)]
|
||||
final class DialogClearedUpdateTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function canBeCreatedFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'update_type' => UpdateType::DialogCleared->value,
|
||||
'timestamp' => 1678886400000,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1678886400000,
|
||||
],
|
||||
'user_locale' => 'ru-ru',
|
||||
];
|
||||
|
||||
$update = DialogClearedUpdate::fromArray($data);
|
||||
|
||||
$this->assertInstanceOf(DialogClearedUpdate::class, $update);
|
||||
$this->assertSame(UpdateType::DialogCleared, $update->updateType);
|
||||
$this->assertSame(123, $update->user->userId);
|
||||
$this->assertSame('John', $update->user->firstName);
|
||||
$this->assertSame('Doe', $update->user->lastName);
|
||||
$this->assertSame('ru-ru', $update->userLocale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogMutedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(DialogMutedUpdate::class)]
|
||||
#[UsesClass(User::class)]
|
||||
final class DialogMutedUpdateTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function canBeCreatedFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'update_type' => UpdateType::DialogMuted->value,
|
||||
'timestamp' => 1678886400000,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1678886400000,
|
||||
],
|
||||
'muted_until' => 1678886400000,
|
||||
'user_locale' => 'ru-ru',
|
||||
];
|
||||
|
||||
$update = DialogMutedUpdate::fromArray($data);
|
||||
|
||||
$this->assertInstanceOf(DialogMutedUpdate::class, $update);
|
||||
$this->assertSame(UpdateType::DialogMuted, $update->updateType);
|
||||
$this->assertSame(123, $update->user->userId);
|
||||
$this->assertSame('John', $update->user->firstName);
|
||||
$this->assertSame('Doe', $update->user->lastName);
|
||||
$this->assertSame(1678886400000, $update->mutedUntil);
|
||||
$this->assertSame('ru-ru', $update->userLocale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogRemovedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(DialogRemovedUpdate::class)]
|
||||
#[UsesClass(User::class)]
|
||||
final class DialogRemovedUpdateTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function canBeCreatedFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'update_type' => UpdateType::DialogRemoved->value,
|
||||
'timestamp' => 1678886400000,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1678886400000,
|
||||
],
|
||||
'user_locale' => 'ru-ru',
|
||||
];
|
||||
|
||||
$update = DialogRemovedUpdate::fromArray($data);
|
||||
|
||||
$this->assertInstanceOf(DialogRemovedUpdate::class, $update);
|
||||
$this->assertSame(UpdateType::DialogRemoved, $update->updateType);
|
||||
$this->assertSame(123, $update->user->userId);
|
||||
$this->assertSame('John', $update->user->firstName);
|
||||
$this->assertSame('Doe', $update->user->lastName);
|
||||
$this->assertSame('ru-ru', $update->userLocale);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace BushlanovDev\MaxMessengerBot\Tests\Models\Updates;
|
||||
|
||||
use BushlanovDev\MaxMessengerBot\Enums\UpdateType;
|
||||
use BushlanovDev\MaxMessengerBot\Models\Updates\DialogUnmutedUpdate;
|
||||
use BushlanovDev\MaxMessengerBot\Models\User;
|
||||
use PHPUnit\Framework\Attributes\CoversClass;
|
||||
use PHPUnit\Framework\Attributes\Test;
|
||||
use PHPUnit\Framework\Attributes\UsesClass;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
#[CoversClass(DialogUnmutedUpdate::class)]
|
||||
#[UsesClass(User::class)]
|
||||
final class DialogUnmutedUpdateTest extends TestCase
|
||||
{
|
||||
#[Test]
|
||||
public function canBeCreatedFromArray(): void
|
||||
{
|
||||
$data = [
|
||||
'update_type' => UpdateType::DialogUnmuted->value,
|
||||
'timestamp' => 1678886400000,
|
||||
'chat_id' => 123,
|
||||
'user' => [
|
||||
'user_id' => 123,
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Doe',
|
||||
'is_bot' => false,
|
||||
'last_activity_time' => 1678886400000,
|
||||
],
|
||||
'user_locale' => 'ru-ru',
|
||||
];
|
||||
|
||||
$update = DialogUnmutedUpdate::fromArray($data);
|
||||
|
||||
$this->assertInstanceOf(DialogUnmutedUpdate::class, $update);
|
||||
$this->assertSame(UpdateType::DialogUnmuted, $update->updateType);
|
||||
$this->assertSame(123, $update->user->userId);
|
||||
$this->assertSame('John', $update->user->firstName);
|
||||
$this->assertSame('Doe', $update->user->lastName);
|
||||
$this->assertSame('ru-ru', $update->userLocale);
|
||||
}
|
||||
}
|
||||
@@ -185,4 +185,43 @@ final class WebhookHandlerTest extends TestCase
|
||||
$handler = new WebhookHandler($this->dispatcher, $this->modelFactoryMock, $this->loggerMock, null);
|
||||
$handler->handle(null);
|
||||
}
|
||||
|
||||
#[Test]
|
||||
public function handleCatchesAndLogsLogicExceptionFromModelFactory(): void
|
||||
{
|
||||
$payload = '{"update_type":"unknown_type","timestamp":123}';
|
||||
$updateData = json_decode($payload, true);
|
||||
$exception = new LogicException('Unknown or unsupported update type received: unknown_type');
|
||||
|
||||
$request = $this->createMockRequest($payload, self::SECRET);
|
||||
|
||||
$this->modelFactoryMock->expects($this->once())
|
||||
->method('createUpdate')
|
||||
->with($updateData)
|
||||
->willThrowException($exception);
|
||||
|
||||
$callIndex = 0;
|
||||
$this->loggerMock->expects($this->exactly(2))
|
||||
->method('debug')
|
||||
->willReturnCallback(
|
||||
function (string $message, array $context = []) use (&$callIndex, $payload, $exception) {
|
||||
if ($callIndex === 0) {
|
||||
$this->assertSame('Received webhook payload', $message);
|
||||
$this->assertArrayHasKey('body', $context);
|
||||
$this->assertSame($payload, $context['body']);
|
||||
} elseif ($callIndex === 1) {
|
||||
$this->assertSame('Unknown or unsupported update type received: unknown_type', $message);
|
||||
$this->assertArrayHasKey('payload', $context);
|
||||
$this->assertArrayHasKey('exception', $context);
|
||||
$this->assertSame($payload, $context['payload']);
|
||||
$this->assertSame($exception, $context['exception']);
|
||||
}
|
||||
$callIndex++;
|
||||
}
|
||||
);
|
||||
|
||||
$handler = new WebhookHandler($this->dispatcher, $this->modelFactoryMock, $this->loggerMock, self::SECRET);
|
||||
|
||||
$handler->handle($request);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user