mirror of
https://github.com/BushlanovDev/max-bot-api-client-php.git
synced 2026-08-19 12:23:00 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ef3b76394c | |||
| 2ecb5c7b04 | |||
| 2e7e9d987f | |||
| cf415b70e9 | |||
| a94dfe0df4 |
+4659
File diff suppressed because one or more lines are too long
+2
-3
@@ -48,7 +48,7 @@ use RuntimeException;
|
||||
*/
|
||||
class Api
|
||||
{
|
||||
public const string LIBRARY_VERSION = '1.3.3';
|
||||
public const string LIBRARY_VERSION = '1.4.0';
|
||||
|
||||
public const string API_VERSION = '1.2.5';
|
||||
|
||||
@@ -134,7 +134,7 @@ class Api
|
||||
}
|
||||
|
||||
$this->client = $client;
|
||||
$this->modelFactory = $modelFactory ?? new ModelFactory();
|
||||
$this->modelFactory = $modelFactory ?? new ModelFactory($this->logger);
|
||||
$this->updateDispatcher = new UpdateDispatcher($this);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -82,8 +82,14 @@ class MaxBotServiceProvider extends ServiceProvider
|
||||
);
|
||||
});
|
||||
|
||||
$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,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+28
-2
@@ -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.
|
||||
*
|
||||
@@ -341,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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
@@ -818,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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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