From c8f87c70b6909185d1e4d07e77f35226adbd2755 Mon Sep 17 00:00:00 2001 From: Alex Date: Sat, 12 Jul 2025 21:39:16 +0300 Subject: [PATCH] Added sendMessage method --- src/Api.php | 49 +++++++++++++++++++++++++++ src/Client.php | 4 +-- src/Enums/MessageFormat.php | 11 ++++++ src/ModelFactory.php | 18 ++++++++++ src/Models/Message.php | 19 +++++++++++ src/Models/MessageBody.php | 23 +++++++++++++ tests/ApiTest.php | 58 ++++++++++++++++++++++++++++++++ tests/ModelFactoryTest.php | 21 ++++++++++++ tests/Models/MessageBodyTest.php | 58 ++++++++++++++++++++++++++++++++ tests/Models/MessageTest.php | 39 +++++++++++++++++++++ 10 files changed, 297 insertions(+), 3 deletions(-) create mode 100644 src/Enums/MessageFormat.php create mode 100644 src/Models/Message.php create mode 100644 src/Models/MessageBody.php create mode 100644 tests/Models/MessageBodyTest.php create mode 100644 tests/Models/MessageTest.php diff --git a/src/Api.php b/src/Api.php index 902b144..56d27ad 100644 --- a/src/Api.php +++ b/src/Api.php @@ -4,11 +4,13 @@ declare(strict_types=1); namespace BushlanovDev\MaxMessengerBot; +use BushlanovDev\MaxMessengerBot\Enums\MessageFormat; use BushlanovDev\MaxMessengerBot\Enums\UpdateType; use BushlanovDev\MaxMessengerBot\Exceptions\ClientApiException; use BushlanovDev\MaxMessengerBot\Exceptions\NetworkException; use BushlanovDev\MaxMessengerBot\Exceptions\SerializationException; use BushlanovDev\MaxMessengerBot\Models\BotInfo; +use BushlanovDev\MaxMessengerBot\Models\Message; use BushlanovDev\MaxMessengerBot\Models\Result; use BushlanovDev\MaxMessengerBot\Models\Subscription; use InvalidArgumentException; @@ -28,6 +30,7 @@ class Api private const string ACTION_ME = '/me'; private const string ACTION_SUBSCRIPTIONS = '/subscriptions'; + private const string ACTION_MESSAGES = '/messages'; private readonly ClientApiInterface $client; @@ -145,4 +148,50 @@ class Api ) ); } + + /** + * Sends a message to a chat. + * + * @param int|null $userId Fill this parameter if you want to send message to user. + * @param int|null $chatId Fill this if you send message to chat. + * @param string|null $text Message text. + * @param bool $notify If false, chat participants would not be notified. + * @param bool $disableLinkPreview If false, server will not generate media preview for links in text. + * + * @return Message + * + * @throws ClientApiException + * @throws NetworkException + * @throws SerializationException + * @throws ReflectionException + */ + public function sendMessage( + ?int $userId = null, + ?int $chatId = null, + ?string $text = null, + ?MessageFormat $format = null, + bool $notify = true, + bool $disableLinkPreview = false, + ): Message { + $query = [ + 'user_id' => $userId, + 'chat_id' => $chatId, + 'disable_link_preview' => $disableLinkPreview, + ]; + + $body = [ + 'text' => $text, + 'format' => $format?->value, + 'notify' => $notify, + ]; + + $response = $this->client->request( + self::METHOD_POST, + self::ACTION_MESSAGES, + array_filter($query, fn($item) => null !== $item), + array_filter($body, fn($item) => null !== $item), + ); + + return $this->modelFactory->createMessage($response['message']); + } } diff --git a/src/Client.php b/src/Client.php index c825379..46b8bb9 100644 --- a/src/Client.php +++ b/src/Client.php @@ -119,7 +119,7 @@ final class Client implements ClientApiInterface $errorCode = $data['code'] ?? 'unknown'; $errorMessage = $data['message'] ?? 'An unknown error occurred.'; - $exception = match ($statusCode) { + throw match ($statusCode) { 401 => new UnauthorizedException($errorMessage, $errorCode, $response), 403 => new ForbiddenException($errorMessage, $errorCode, $response), 404 => new NotFoundException($errorMessage, $errorCode, $response), @@ -127,7 +127,5 @@ final class Client implements ClientApiInterface 429 => new RateLimitExceededException($errorMessage, $errorCode, $response), default => new ClientApiException($errorMessage, $errorCode, $response, $statusCode), }; - - throw $exception; } } diff --git a/src/Enums/MessageFormat.php b/src/Enums/MessageFormat.php new file mode 100644 index 0000000..3978536 --- /dev/null +++ b/src/Enums/MessageFormat.php @@ -0,0 +1,11 @@ + $data * * @return Result + * * @throws ReflectionException */ public function createResult(array $data): Result @@ -33,6 +35,7 @@ class ModelFactory * @param array $data * * @return BotInfo + * * @throws ReflectionException */ public function createBotInfo(array $data): BotInfo @@ -46,6 +49,7 @@ class ModelFactory * @param array $data * * @return Subscription + * * @throws ReflectionException */ public function createSubscription(array $data): Subscription @@ -67,4 +71,18 @@ class ModelFactory ? array_map([$this, 'createSubscription'], $data['subscriptions']) : []; } + + /** + * Message. + * + * @param array $data + * + * @return Message + * + * @throws ReflectionException + */ + public function createMessage(array $data): Message + { + return Message::fromArray($data); + } } diff --git a/src/Models/Message.php b/src/Models/Message.php new file mode 100644 index 0000000..7aa5e04 --- /dev/null +++ b/src/Models/Message.php @@ -0,0 +1,19 @@ +api->unsubscribe($url); $this->assertSame($expectedResultObject, $result); } + + #[Test] + public function sendMessageBuildsCorrectRequestForAllParameters(): void + { + $chatId = 123456; + $text = 'Hello, **world**!'; + $format = MessageFormat::Markdown; + $notify = false; + $disableLinkPreview = true; + + $expectedQuery = [ + 'chat_id' => $chatId, + 'disable_link_preview' => $disableLinkPreview, + ]; + + $expectedBody = [ + 'text' => $text, + 'format' => $format->value, + 'notify' => $notify, + ]; + + $apiResponse = [ + 'message' => [ + 'body' => ['mid' => 'mid.456.xyz', 'seq' => 101, 'text' => $text], + 'timestamp' => time(), + ] + ]; + + $expectedMessageObject = Message::fromArray($apiResponse['message']); + + $this->clientMock + ->expects($this->once()) + ->method('request') + ->with('POST', '/messages', $expectedQuery, $expectedBody) + ->willReturn($apiResponse); + + $this->modelFactoryMock + ->expects($this->once()) + ->method('createMessage') + ->with($apiResponse['message']) + ->willReturn($expectedMessageObject); + + $result = $this->api->sendMessage( + null, + $chatId, + $text, + $format, + $notify, + $disableLinkPreview, + ); + + $this->assertSame($expectedMessageObject, $result); + } } diff --git a/tests/ModelFactoryTest.php b/tests/ModelFactoryTest.php index d4f3f78..5c9861e 100644 --- a/tests/ModelFactoryTest.php +++ b/tests/ModelFactoryTest.php @@ -9,6 +9,8 @@ use BushlanovDev\MaxMessengerBot\Enums\UpdateType; use BushlanovDev\MaxMessengerBot\ModelFactory; use BushlanovDev\MaxMessengerBot\Models\BotCommand; use BushlanovDev\MaxMessengerBot\Models\BotInfo; +use BushlanovDev\MaxMessengerBot\Models\Message; +use BushlanovDev\MaxMessengerBot\Models\MessageBody; use BushlanovDev\MaxMessengerBot\Models\Result; use BushlanovDev\MaxMessengerBot\Models\Subscription; use PHPUnit\Framework\Attributes\CoversClass; @@ -22,6 +24,8 @@ use PHPUnit\Framework\TestCase; #[UsesClass(Result::class)] #[UsesClass(Subscription::class)] #[UsesClass(ArrayOf::class)] +#[UsesClass(Message::class)] +#[UsesClass(MessageBody::class)] final class ModelFactoryTest extends TestCase { private ModelFactory $factory; @@ -134,4 +138,21 @@ final class ModelFactoryTest extends TestCase $this->assertInstanceOf(Subscription::class, $subscriptions[0]); $this->assertSame(UpdateType::MessageCreated, $subscriptions[0]->updateTypes[0]); } + + #[Test] + public function createMessage(): void + { + $rawData = [ + 'body' => [ + 'mid' => 'mid.456.xyz', + 'seq' => 101, + 'text' => 'Hello, **world**!', + ], + ]; + + $message = $this->factory->createMessage($rawData); + + $this->assertInstanceOf(Message::class, $message); + $this->assertInstanceOf(MessageBody::class, $message->body); + } } diff --git a/tests/Models/MessageBodyTest.php b/tests/Models/MessageBodyTest.php new file mode 100644 index 0000000..d38d667 --- /dev/null +++ b/tests/Models/MessageBodyTest.php @@ -0,0 +1,58 @@ + 'mid.456.xyz', + 'seq' => 101, + 'text' => 'Hello, **world**!', + ]; + + $messageBody = MessageBody::fromArray($data); + + $this->assertInstanceOf(MessageBody::class, $messageBody); + $this->assertSame($data['mid'], $messageBody->mid); + $this->assertSame($data['seq'], $messageBody->seq); + $this->assertSame($data['text'], $messageBody->text); + + $array = $messageBody->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } + + #[Test] + public function canBeCreatedFromArrayWithOptionalDataNull(): void + { + $data = [ + 'mid' => 'mid.456.xyz', + 'seq' => 101, + 'text' => null, + ]; + + $messageBody = MessageBody::fromArray($data); + + $this->assertInstanceOf(MessageBody::class, $messageBody); + $this->assertSame($data['mid'], $messageBody->mid); + $this->assertSame($data['seq'], $messageBody->seq); + $this->assertNull($messageBody->text); + + $array = $messageBody->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } +} diff --git a/tests/Models/MessageTest.php b/tests/Models/MessageTest.php new file mode 100644 index 0000000..35450ad --- /dev/null +++ b/tests/Models/MessageTest.php @@ -0,0 +1,39 @@ + [ + 'mid' => 'mid.456.xyz', + 'seq' => 101, + 'text' => 'Hello, **world**!', + ], + ]; + + $message = Message::fromArray($data); + + $this->assertInstanceOf(Message::class, $message); + $this->assertInstanceOf(MessageBody::class, $message->body); + + $array = $message->toArray(); + + $this->assertIsArray($array); + $this->assertSame($data, $array); + } +}