Merge pull request #43 from smotim/fix/me-commands

feat: add editBotCommands for PATCH /me/commands, deprecate editBotInfo
This commit is contained in:
Alex
2026-10-01 08:33:26 +03:00
committed by GitHub
9 changed files with 213 additions and 3 deletions
+2 -1
View File
@@ -181,7 +181,8 @@ $handler->handle();
#### Bots
- [x] `GET /me` (`getBotInfo`) - [*Получение информации о боте.*](./docs/README.md#Получение-информации-о-боте)
- [x] `PATCH /me` (`editBotInfo`) - [*Редактирование информации о боте.*](./docs/README.md#Редактирование-информации-о-боте)
- [x] `PATCH /me/commands` (`editBotCommands`) - [*Редактирование команд бота.*](./docs/README.md#Редактирование-команд-бота)
- [x] ~~`PATCH /me` (`editBotInfo`) - [*Редактирование информации о боте.*](./docs/README.md#Редактирование-информации-о-боте-deprecated)~~ (deprecated)
#### Chats
+18 -2
View File
@@ -4,7 +4,8 @@
- [Инициализация бота](#Инициализация-бота)
- [Информация о боте](#Информация-о-боте)
- `GET /me` (`getBotInfo`) - [*Получение информации о боте.*](#Получение-информации-о-боте)
- `PATCH /me` (`editBotInfo`) - [*Редактирование информации о боте.*](#Редактирование-информации-о-боте)
- `PATCH /me/commands` (`editBotCommands`) - [*Редактирование команд бота.*](#Редактирование-команд-бота)
- ~~`PATCH /me` (`editBotInfo`)~~ - [*Редактирование информации о боте.*](#Редактирование-информации-о-боте-deprecated) (deprecated)
- [Чаты](#Чаты)
- ~~`GET /chats` (`getChats`)~~ - [*Получение списка всех чатов бота.*](#Получение-списка-всех-чатов-бота-deprecated) (deprecated)
- `GET /chats/{chatLink}` (`getChatByLink`) - [*Получение информации о чате по ссылке.*](#Получение-информации-о-чате-по-ссылке)
@@ -119,7 +120,22 @@ $api = new BushlanovDev\MaxMessengerBot\Api(
$botInfo = $api->getBotInfo();
```
### Редактирование информации о боте
### Редактирование команд бота
Задаёт команды, которые пользователь видит подсказками, когда набирает «/». Не больше 32 команд, описание команды
необязательно.
```php
$botCommands = $api->editBotCommands([
new BotCommand('start', 'Начать'),
new BotCommand('help', 'Помощь'),
]);
```
### Редактирование информации о боте (deprecated)
API больше не принимает `PATCH /me` и отвечает `Path /me is not recognized`. Команды задаются методом
[`editBotCommands`](#Редактирование-команд-бота), имя, описание и аватар бота — на платформе MAX для партнёров.
Обратите внимание, что данный метод отправляется PATCH запросом. Это значит, что будут обновлены только переданные
поля.
+32
View File
@@ -17,6 +17,8 @@ use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\AudioAttachmentRequ
use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\FileAttachmentRequest;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\PhotoAttachmentRequest;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\VideoAttachmentRequest;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotCommandsInfo;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\BotPatch;
use BushlanovDev\MaxMessengerBot\Models\Chat;
@@ -61,6 +63,7 @@ class Api
private const string METHOD_PUT = 'PUT';
private const string ACTION_ME = '/me';
private const string ACTION_ME_COMMANDS = '/me/commands';
private const string ACTION_SUBSCRIPTIONS = '/subscriptions';
private const string ACTION_MESSAGES = '/messages';
private const string ACTION_UPLOADS = '/uploads';
@@ -252,6 +255,31 @@ class Api
);
}
/**
* Edits the bot commands shown to users as hints when they type "/".
*
* Example: editBotCommands([new BotCommand('start', 'Start the bot'), new BotCommand('help', 'Help')]);
*
* @param BotCommand[] $commands Up to 32 commands.
*
* @return BotCommandsInfo
* @throws ClientApiException
* @throws NetworkException
* @throws ReflectionException
* @throws SerializationException
*/
public function editBotCommands(array $commands): BotCommandsInfo
{
return $this->modelFactory->createBotCommandsInfo(
$this->client->request(
self::METHOD_PATCH,
self::ACTION_ME_COMMANDS,
[],
['commands' => array_map(static fn(BotCommand $command) => $command->toArray(), $commands)],
)
);
}
/**
* List of all active webhook subscriptions.
*
@@ -1099,6 +1127,10 @@ class Api
*
* Example: editBotInfo(new BotPatch(name: 'New Bot Name', description: null));
*
* @deprecated The API no longer accepts `PATCH /me` and answers "Path /me is not recognized".
* Set commands with editBotCommands(); name, description and photo are edited
* on the MAX partner platform.
*
* @param BotPatch $botPatch
*
* @return BotInfo
+14
View File
@@ -32,6 +32,7 @@ use BushlanovDev\MaxMessengerBot\Models\Attachments\PhotoAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ShareAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\StickerAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\VideoAttachment;
use BushlanovDev\MaxMessengerBot\Models\BotCommandsInfo;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Chat;
use BushlanovDev\MaxMessengerBot\Models\ChatList;
@@ -106,6 +107,19 @@ readonly class ModelFactory
return Result::fromArray($data);
}
/**
* Commands of the current bot.
*
* @param array<string, mixed> $data
*
* @return BotCommandsInfo
* @throws ReflectionException
*/
public function createBotCommandsInfo(array $data): BotCommandsInfo
{
return BotCommandsInfo::fromArray($data);
}
/**
* Information about the current bot.
*
+22
View File
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
/**
* Bot commands information.
*/
final readonly class BotCommandsInfo extends AbstractModel
{
/**
* @param BotCommand[]|null $commands Commands supported by the bot (up to 32 elements).
*/
public function __construct(
#[ArrayOf(BotCommand::class)]
public ?array $commands,
) {
}
}
+2
View File
@@ -17,6 +17,8 @@ use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\PhotoAttachmentRequ
* @property-read BotCommand[]|null $commands
* @property-read PhotoAttachmentRequestPayload|null $photo
* @property-read string|null $name @deprecated Use first_name
*
* @deprecated The API no longer accepts `PATCH /me`. Use Api::editBotCommands() for commands.
*/
final readonly class BotPatch extends AbstractPatchModel
{
+56
View File
@@ -35,6 +35,8 @@ use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\PhotoAttachmentRequ
use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\ShareAttachmentRequest;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\StickerAttachmentRequest;
use BushlanovDev\MaxMessengerBot\Models\Attachments\Requests\VideoAttachmentRequest;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotCommandsInfo;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\BotPatch;
use BushlanovDev\MaxMessengerBot\Models\Chat;
@@ -116,6 +118,8 @@ use RuntimeException;
#[UsesClass(ChatMembersList::class)]
#[UsesClass(ChatAdmin::class)]
#[UsesClass(BotPatch::class)]
#[UsesClass(BotCommand::class)]
#[UsesClass(BotCommandsInfo::class)]
#[UsesClass(ChatPatch::class)]
#[UsesClass(VideoAttachmentDetails::class)]
#[UsesClass(VideoUrls::class)]
@@ -1883,6 +1887,58 @@ final class ApiTest extends TestCase
$this->assertSame($expectedResult, $result);
}
#[Test]
public function editBotCommandsSendsCommandsToDedicatedEndpoint(): void
{
$rawResponseData = [
'commands' => [
['name' => 'start', 'description' => 'Start the bot'],
['name' => 'help', 'description' => null],
],
];
$expectedResult = new BotCommandsInfo([
new BotCommand('start', 'Start the bot'),
new BotCommand('help', null),
]);
$this->clientMock
->expects($this->once())
->method('request')
->with('PATCH', '/me/commands', [], $rawResponseData)
->willReturn($rawResponseData);
$this->modelFactoryMock
->expects($this->once())
->method('createBotCommandsInfo')
->with($rawResponseData)
->willReturn($expectedResult);
$result = $this->api->editBotCommands([
new BotCommand('start', 'Start the bot'),
new BotCommand('help', null),
]);
$this->assertSame($expectedResult, $result);
}
#[Test]
public function editBotCommandsSendsAnEmptyList(): void
{
$this->clientMock
->expects($this->once())
->method('request')
->with('PATCH', '/me/commands', [], ['commands' => []])
->willReturn(['commands' => []]);
$this->modelFactoryMock
->expects($this->once())
->method('createBotCommandsInfo')
->with(['commands' => []])
->willReturn(new BotCommandsInfo([]));
$this->api->editBotCommands([]);
}
#[Test]
public function editBotInfoSendsCorrectPatchBody(): void
{
+19
View File
@@ -27,6 +27,7 @@ use BushlanovDev\MaxMessengerBot\Models\Attachments\Payloads\ShareAttachmentRequ
use BushlanovDev\MaxMessengerBot\Models\Attachments\PhotoAttachment;
use BushlanovDev\MaxMessengerBot\Models\Attachments\ShareAttachment;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotCommandsInfo;
use BushlanovDev\MaxMessengerBot\Models\BotInfo;
use BushlanovDev\MaxMessengerBot\Models\Chat;
use BushlanovDev\MaxMessengerBot\Models\ChatList;
@@ -61,6 +62,7 @@ use Psr\Log\LoggerInterface;
#[CoversClass(ModelFactory::class)]
#[UsesClass(BotInfo::class)]
#[UsesClass(BotCommand::class)]
#[UsesClass(BotCommandsInfo::class)]
#[UsesClass(Result::class)]
#[UsesClass(Subscription::class)]
#[UsesClass(ArrayOf::class)]
@@ -171,6 +173,23 @@ final class ModelFactoryTest extends TestCase
$this->assertSame('help', $botInfo->commands[1]->name);
}
#[Test]
public function createBotCommandsInfoHydratesCommands(): void
{
$botCommands = $this->factory->createBotCommandsInfo([
'commands' => [
['name' => 'start', 'description' => 'Start the bot'],
['name' => 'help', 'description' => null],
],
]);
$this->assertInstanceOf(BotCommandsInfo::class, $botCommands);
$this->assertCount(2, $botCommands->commands);
$this->assertInstanceOf(BotCommand::class, $botCommands->commands[0]);
$this->assertSame('start', $botCommands->commands[0]->name);
$this->assertNull($botCommands->commands[1]->description);
}
#[Test]
public function createBotInfoHandlesNullCommands(): void
{
+48
View File
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace BushlanovDev\MaxMessengerBot\Tests\Models;
use BushlanovDev\MaxMessengerBot\Attributes\ArrayOf;
use BushlanovDev\MaxMessengerBot\Models\BotCommand;
use BushlanovDev\MaxMessengerBot\Models\BotCommandsInfo;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\UsesClass;
use PHPUnit\Framework\TestCase;
#[CoversClass(BotCommandsInfo::class)]
#[UsesClass(BotCommand::class)]
#[UsesClass(ArrayOf::class)]
final class BotCommandsInfoTest extends TestCase
{
#[Test]
public function canBeCreatedFromArray(): void
{
$info = BotCommandsInfo::fromArray([
'commands' => [
['name' => 'start', 'description' => 'Start the bot'],
],
]);
$this->assertCount(1, $info->commands);
$this->assertInstanceOf(BotCommand::class, $info->commands[0]);
$this->assertSame('start', $info->commands[0]->name);
$this->assertSame('Start the bot', $info->commands[0]->description);
}
#[Test]
public function commandsAreNullWhenTheBotHasNone(): void
{
$this->assertNull(BotCommandsInfo::fromArray([])->commands);
}
#[Test]
public function toArraySerializesCommands(): void
{
$info = new BotCommandsInfo([new BotCommand('help', 'Help')]);
$this->assertSame(['commands' => [['name' => 'help', 'description' => 'Help']]], $info->toArray());
}
}