Skip to content

Commit 46e7c2e

Browse files
committed
Added pinMessage & getMessages & deleteMessage & getMessageById
1 parent a3dd4d9 commit 46e7c2e

File tree

5 files changed

+342
-5
lines changed

5 files changed

+342
-5
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
- [x] `DELETE /chats/{chatId}` (`deleteChat`) — *Удаление чата.*
3838
- [x] `POST /chats/{chatId}/actions` (`sendAction`) — *Отправка действия в чат (например, "печатает...").*
3939
- [x] `GET /chats/{chatId}/pin` (`getPinnedMessage`) — *Получение закрепленного сообщения.*
40-
- [ ] `PUT /chats/{chatId}/pin` (`pinMessage`) — *Закрепление сообщения.*
40+
- [x] `PUT /chats/{chatId}/pin` (`pinMessage`) — *Закрепление сообщения.*
4141
- [x] `DELETE /chats/{chatId}/pin` (`unpinMessage`) — *Открепление сообщения.*
4242
- [x] `GET /chats/{chatId}/members/me` (`getMembership`) — *Получение информации о членстве бота в чате.*
4343
- [x] `DELETE /chats/{chatId}/members/me` (`leaveChat`) — *Выход бота из чата.*
@@ -61,11 +61,11 @@
6161

6262
#### Messages
6363

64-
- [ ] `GET /messages` (`getMessages`) — *Получение списка сообщений из чата.*
64+
- [x] `GET /messages` (`getMessages`) — *Получение списка сообщений из чата.*
6565
- [x] `POST /messages` (`sendMessage`) — *Отправка сообщения.*
6666
- [ ] `PUT /messages` (`editMessage`) — *Редактирование сообщения.*
67-
- [ ] `DELETE /messages` (`deleteMessage`) — *Удаление сообщения.*
68-
- [ ] `GET /messages/{messageId}` (`getMessageById`) — *Получение сообщения по ID.*
67+
- [x] `DELETE /messages` (`deleteMessage`) — *Удаление сообщения.*
68+
- [x] `GET /messages/{messageId}` (`getMessageById`) — *Получение сообщения по ID.*
6969
- [ ] `GET /videos/{videoToken}` (`getVideoAttachmentDetails`) — *Получение детальной информации о видео.*
7070
- [ ] `POST /answers` (`answerOnCallback`) — *Ответ на нажатие callback-кнопки.*
7171

src/Api.php

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Api
5050
private const string METHOD_POST = 'POST';
5151
private const string METHOD_DELETE = 'DELETE';
5252
// private const string METHOD_PATCH = 'PATCH';
53+
private const string METHOD_PUT = 'PUT';
5354

5455
private const string ACTION_ME = '/me';
5556
private const string ACTION_SUBSCRIPTIONS = '/subscriptions';
@@ -653,7 +654,7 @@ public function getMembership(int $chatId): ChatMember
653654
*
654655
* @param int $chatId Chat identifier to leave from.
655656
*
656-
* @return Result A simple success/fail result.
657+
* @return Result
657658
* @throws ClientApiException
658659
* @throws NetworkException
659660
* @throws ReflectionException
@@ -668,4 +669,114 @@ public function leaveChat(int $chatId): Result
668669
)
669670
);
670671
}
672+
673+
/**
674+
* Returns messages in a chat. Messages are traversed in reverse chronological order.
675+
*
676+
* @param int $chatId Identifier of the chat to get messages from.
677+
* @param string[]|null $messageIds A comma-separated list of message IDs to retrieve.
678+
* @param int|null $from Start time (Unix timestamp in ms) for the requested messages.
679+
* @param int|null $to End time (Unix timestamp in ms) for the requested messages.
680+
* @param int|null $count Maximum amount of messages in the response (1-100, default 50).
681+
*
682+
* @return Message[]
683+
* @throws ClientApiException
684+
* @throws NetworkException
685+
* @throws ReflectionException
686+
* @throws SerializationException
687+
*/
688+
public function getMessages(
689+
int $chatId,
690+
?array $messageIds = null,
691+
?int $from = null,
692+
?int $to = null,
693+
?int $count = null,
694+
): array {
695+
$query = [
696+
'chat_id' => $chatId,
697+
'message_ids' => $messageIds !== null ? implode(',', $messageIds) : null,
698+
'from' => $from,
699+
'to' => $to,
700+
'count' => $count,
701+
];
702+
703+
$response = $this->client->request(
704+
self::METHOD_GET,
705+
self::ACTION_MESSAGES,
706+
array_filter($query, fn ($value) => $value !== null)
707+
);
708+
709+
return $this->modelFactory->createMessages($response);
710+
}
711+
712+
/**
713+
* Deletes a message in a dialog or in a chat if the bot has permission to delete messages.
714+
*
715+
* @param string $messageId Identifier of the message to be deleted.
716+
*
717+
* @return Result
718+
* @throws ClientApiException
719+
* @throws NetworkException
720+
* @throws ReflectionException
721+
* @throws SerializationException
722+
*/
723+
public function deleteMessage(string $messageId): Result
724+
{
725+
return $this->modelFactory->createResult(
726+
$this->client->request(
727+
self::METHOD_DELETE,
728+
self::ACTION_MESSAGES,
729+
['message_id' => $messageId],
730+
)
731+
);
732+
}
733+
734+
/**
735+
* Returns a single message by its identifier.
736+
*
737+
* @param string $messageId Message identifier (`mid`) to get.
738+
*
739+
* @return Message
740+
* @throws ClientApiException
741+
* @throws NetworkException
742+
* @throws ReflectionException
743+
* @throws SerializationException
744+
*/
745+
public function getMessageById(string $messageId): Message
746+
{
747+
return $this->modelFactory->createMessage(
748+
$this->client->request(
749+
self::METHOD_GET,
750+
self::ACTION_MESSAGES . '/' . $messageId,
751+
)
752+
);
753+
}
754+
755+
/**
756+
* Pins a message in a chat or channel.
757+
*
758+
* @param int $chatId Chat identifier where the message should be pinned.
759+
* @param string $messageId Identifier of the message to pin.
760+
* @param bool $notify If true, participants will be notified with a system message.
761+
*
762+
* @return Result
763+
* @throws ClientApiException
764+
* @throws NetworkException
765+
* @throws ReflectionException
766+
* @throws SerializationException
767+
*/
768+
public function pinMessage(int $chatId, string $messageId, bool $notify = true): Result
769+
{
770+
return $this->modelFactory->createResult(
771+
$this->client->request(
772+
self::METHOD_PUT,
773+
sprintf(self::ACTION_CHATS_PIN, $chatId),
774+
[],
775+
[
776+
'message_id' => $messageId,
777+
'notify' => $notify,
778+
]
779+
)
780+
);
781+
}
671782
}

src/ModelFactory.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ public function createMessage(array $data): Message
101101
return Message::fromArray($data);
102102
}
103103

104+
/**
105+
* List of messages.
106+
*
107+
* @param array<string, mixed> $data
108+
*
109+
* @return Message[]
110+
*/
111+
public function createMessages(array $data): array
112+
{
113+
return isset($data['messages']) && is_array($data['messages'])
114+
? array_map([$this, 'createMessage'], $data['messages'])
115+
: [];
116+
}
117+
104118
/**
105119
* Endpoint you should upload to your binaries.
106120
*

tests/ApiTest.php

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,4 +1500,183 @@ public function leaveChatCallsClientCorrectly(): void
15001500

15011501
$this->assertSame($expectedResult, $result);
15021502
}
1503+
1504+
#[Test]
1505+
public function getMessagesCallsClientWithAllParameters(): void
1506+
{
1507+
$chatId = 12345;
1508+
$messageIds = ['mid.1', 'mid.2'];
1509+
$from = 1678880000;
1510+
$to = 1678886400;
1511+
$count = 10;
1512+
1513+
$expectedQuery = [
1514+
'chat_id' => $chatId,
1515+
'message_ids' => 'mid.1,mid.2',
1516+
'from' => $from,
1517+
'to' => $to,
1518+
'count' => $count,
1519+
];
1520+
1521+
$messageData = [
1522+
'timestamp' => 1,
1523+
'body' => ['mid' => 'mid.1', 'seq' => 1],
1524+
'recipient' => ['chat_type' => 'chat', 'chat_id' => $chatId],
1525+
];
1526+
$rawResponse = ['messages' => [$messageData]];
1527+
$expectedMessages = [Message::fromArray($messageData)];
1528+
1529+
$this->clientMock->expects($this->once())
1530+
->method('request')
1531+
->with('GET', '/messages', $expectedQuery)
1532+
->willReturn($rawResponse);
1533+
1534+
$this->modelFactoryMock->expects($this->once())
1535+
->method('createMessages')
1536+
->with($rawResponse)
1537+
->willReturn($expectedMessages);
1538+
1539+
$result = $this->api->getMessages($chatId, $messageIds, $from, $to, $count);
1540+
1541+
$this->assertIsArray($result);
1542+
$this->assertSame($expectedMessages, $result);
1543+
}
1544+
1545+
#[Test]
1546+
public function getMessagesReturnsEmptyArrayForEmptyResponse(): void
1547+
{
1548+
$chatId = 54321;
1549+
$rawResponse = ['messages' => []];
1550+
1551+
$this->clientMock->expects($this->once())
1552+
->method('request')
1553+
->with('GET', '/messages', ['chat_id' => $chatId])
1554+
->willReturn($rawResponse);
1555+
1556+
$this->modelFactoryMock->expects($this->once())
1557+
->method('createMessages')
1558+
->with($rawResponse)
1559+
->willReturn([]);
1560+
1561+
$result = $this->api->getMessages($chatId);
1562+
1563+
$this->assertIsArray($result);
1564+
$this->assertEmpty($result);
1565+
}
1566+
1567+
#[Test]
1568+
public function deleteMessageCallsClientCorrectly(): void
1569+
{
1570+
$messageId = 'mid.12345.abcdef';
1571+
$expectedQuery = ['message_id' => $messageId];
1572+
$rawResponse = ['success' => true];
1573+
$expectedResult = new Result(true, null);
1574+
1575+
$this->clientMock
1576+
->expects($this->once())
1577+
->method('request')
1578+
->with(
1579+
self::equalTo('DELETE'),
1580+
self::equalTo('/messages'),
1581+
self::equalTo($expectedQuery),
1582+
)
1583+
->willReturn($rawResponse);
1584+
1585+
$this->modelFactoryMock
1586+
->expects($this->once())
1587+
->method('createResult')
1588+
->with($rawResponse)
1589+
->willReturn($expectedResult);
1590+
1591+
$result = $this->api->deleteMessage($messageId);
1592+
1593+
$this->assertSame($expectedResult, $result);
1594+
}
1595+
1596+
#[Test]
1597+
public function getMessageByIdCallsClientAndFactoryCorrectly(): void
1598+
{
1599+
$messageId = 'mid.abcdef.123456';
1600+
$uri = sprintf('/messages/%s', $messageId);
1601+
1602+
$rawResponse = [
1603+
'timestamp' => 1679000000,
1604+
'body' => ['mid' => $messageId, 'seq' => 123, 'text' => 'This is a specific message.'],
1605+
'recipient' => ['chat_type' => 'dialog', 'user_id' => 101],
1606+
];
1607+
$expectedMessage = Message::fromArray($rawResponse);
1608+
1609+
$this->clientMock
1610+
->expects($this->once())
1611+
->method('request')
1612+
->with(self::equalTo('GET'), self::equalTo($uri))
1613+
->willReturn($rawResponse);
1614+
1615+
$this->modelFactoryMock
1616+
->expects($this->once())
1617+
->method('createMessage')
1618+
->with($rawResponse)
1619+
->willReturn($expectedMessage);
1620+
1621+
$result = $this->api->getMessageById($messageId);
1622+
1623+
$this->assertSame($expectedMessage, $result);
1624+
}
1625+
1626+
#[Test]
1627+
public function pinMessageCallsClientWithCorrectBody(): void
1628+
{
1629+
$chatId = 12345;
1630+
$messageId = 'mid.to.pin';
1631+
$notify = false;
1632+
$uri = sprintf('/chats/%d/pin', $chatId);
1633+
1634+
$expectedBody = ['message_id' => $messageId, 'notify' => $notify];
1635+
$rawResponse = ['success' => true];
1636+
$expectedResult = new Result(true, null);
1637+
1638+
$this->clientMock
1639+
->expects($this->once())
1640+
->method('request')
1641+
->with(
1642+
self::equalTo('PUT'),
1643+
self::equalTo($uri),
1644+
self::equalTo([]),
1645+
self::equalTo($expectedBody),
1646+
)
1647+
->willReturn($rawResponse);
1648+
1649+
$this->modelFactoryMock
1650+
->expects($this->once())
1651+
->method('createResult')
1652+
->with($rawResponse)
1653+
->willReturn($expectedResult);
1654+
1655+
$result = $this->api->pinMessage($chatId, $messageId, $notify);
1656+
$this->assertSame($expectedResult, $result);
1657+
}
1658+
1659+
#[Test]
1660+
public function pinMessageUsesDefaultNotificationValue(): void
1661+
{
1662+
$chatId = 54321;
1663+
$messageId = 'mid.another.pin';
1664+
$uri = sprintf('/chats/%d/pin', $chatId);
1665+
1666+
$expectedBody = ['message_id' => $messageId, 'notify' => true];
1667+
$rawResponse = ['success' => true];
1668+
$expectedResult = new Result(true, null);
1669+
1670+
$this->clientMock
1671+
->expects($this->once())
1672+
->method('request')
1673+
->with('PUT', $uri, [], $expectedBody)
1674+
->willReturn($rawResponse);
1675+
1676+
$this->modelFactoryMock
1677+
->method('createResult')
1678+
->willReturn($expectedResult);
1679+
1680+
$this->api->pinMessage($chatId, $messageId);
1681+
}
15031682
}

tests/ModelFactoryTest.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,37 @@ public function createChatMember()
386386
$this->assertSame(ChatAdminPermission::Write, $chatMember->permissions[1]);
387387
$this->assertEquals($rawData, $chatMember->toArray());
388388
}
389+
390+
#[Test]
391+
public function createMessagesReturnsArrayOfMessageObjects(): void
392+
{
393+
$data = [
394+
'messages' => [
395+
[
396+
'timestamp' => 1,
397+
'body' => ['mid' => 'mid.1', 'seq' => 1],
398+
'recipient' => ['chat_type' => 'chat', 'chat_id' => 123],
399+
],
400+
[
401+
'timestamp' => 2,
402+
'body' => ['mid' => 'mid.2', 'seq' => 2],
403+
'recipient' => ['chat_type' => 'chat', 'chat_id' => 123],
404+
],
405+
],
406+
];
407+
408+
$messages = $this->factory->createMessages($data);
409+
410+
$this->assertIsArray($messages);
411+
$this->assertCount(2, $messages);
412+
$this->assertInstanceOf(Message::class, $messages[0]);
413+
$this->assertSame('mid.1', $messages[0]->body->mid);
414+
}
415+
416+
#[Test]
417+
public function createMessagesHandlesEmptyOrMissingKey(): void
418+
{
419+
$this->assertEmpty($this->factory->createMessages(['messages' => []]));
420+
$this->assertEmpty($this->factory->createMessages([]));
421+
}
389422
}

0 commit comments

Comments
 (0)