diff --git a/CHANGELOG.md b/CHANGELOG.md index 54959b52..631d5271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,24 @@ # Telegram Bot API for PHP Change Log -## 0.17.1 under development +## 0.18 April 4, 2026 - Enh #190: Open file in binary mode in `InputFile::fromLocalFile()` method. +- New #191: Add `GetManagedBotToken`, `ReplaceManagedBotToken` and `SavePreparedKeyboardButton` methods. +- New #191: Add `KeyboardButtonRequestManagedBot`, `ManagedBotCreated`, `ManagedBotUpdated`, `PreparedKeyboardButton`, + `PollOptionAdded` and `PollOptionDeleted` types. +- New #191: Add `requestManagedBot` field to `KeyboardButton` type. +- New #191: Add `canManageBots` field to `User` type. +- New #191: Add `managedBotCreated`, `pollOptionAdded`, `pollOptionDeleted` and `replyToPollOptionId` field to `Message` + type. +- New #191: Add `managedBot` field to `Update` type. +- Chg #191: Replace `correctOptionId` field with `correctOptionIds` in `Poll` type. +- New #191: Add `allowsRevoting`, `description` and `descriptionEntities` fields to `Poll` type. +- Chg #191: Replace `correctOptionId` parameter with `correctOptionIds` in `SendPoll` method. +- New #191: Add `allowsRevoting`, `shuffleOptions`, `allowAddingOptions`, `hideResultsUntilCloses`, `description`, + `descriptionParseMode` and `descriptionEntities` parameters to `SendPoll` method. +- New #191: Add `persistentId`, `addedByUser`, `addedByChat` and `additionDate` fields to `PollOption` type. +- New #191: Add `optionPersistentIds` field to `PollAnswer` type. +- New #191: Add `pollOptionId` field to `ReplyParameters` type. ## 0.17 March 1, 2026 diff --git a/README.md b/README.md index 40a284d2..deb2cc43 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The package provides a simple and convenient way to interact with the Telegram Bot API. -✔️ Telegram Bot API 9.5 (March 1, 2026) is **fully supported**. +✔️ Telegram Bot API 9.6 (April 3, 2026) is **fully supported**. ♻️ **Zero dependencies** — no third-party libraries, only native PHP. diff --git a/src/Method/GetManagedBotToken.php b/src/Method/GetManagedBotToken.php new file mode 100644 index 00000000..29618db5 --- /dev/null +++ b/src/Method/GetManagedBotToken.php @@ -0,0 +1,41 @@ + + */ +final readonly class GetManagedBotToken implements MethodInterface +{ + public function __construct( + private int $userId, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'getManagedBotToken'; + } + + public function getData(): array + { + return ['user_id' => $this->userId]; + } + + public function getResultType(): StringValue + { + return new StringValue(); + } +} diff --git a/src/Method/ReplaceManagedBotToken.php b/src/Method/ReplaceManagedBotToken.php new file mode 100644 index 00000000..96e94593 --- /dev/null +++ b/src/Method/ReplaceManagedBotToken.php @@ -0,0 +1,41 @@ + + */ +final readonly class ReplaceManagedBotToken implements MethodInterface +{ + public function __construct( + private int $userId, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'replaceManagedBotToken'; + } + + public function getData(): array + { + return ['user_id' => $this->userId]; + } + + public function getResultType(): StringValue + { + return new StringValue(); + } +} diff --git a/src/Method/SavePreparedKeyboardButton.php b/src/Method/SavePreparedKeyboardButton.php new file mode 100644 index 00000000..7f2d3904 --- /dev/null +++ b/src/Method/SavePreparedKeyboardButton.php @@ -0,0 +1,47 @@ + + */ +final readonly class SavePreparedKeyboardButton implements MethodInterface +{ + public function __construct( + private int $userId, + private KeyboardButton $button, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'savePreparedKeyboardButton'; + } + + public function getData(): array + { + return [ + 'user_id' => $this->userId, + 'button' => $this->button->toRequestArray(), + ]; + } + + public function getResultType(): ObjectValue + { + return new ObjectValue(PreparedKeyboardButton::class); + } +} diff --git a/src/Method/SendPoll.php b/src/Method/SendPoll.php index 8e85220c..ac9c6933 100644 --- a/src/Method/SendPoll.php +++ b/src/Method/SendPoll.php @@ -27,7 +27,9 @@ /** * @param InputPollOption[] $options * @param MessageEntity[]|null $questionEntities + * @param int[]|null $correctOptionIds * @param MessageEntity[]|null $explanationEntities + * @param MessageEntity[]|null $descriptionEntities */ public function __construct( private int|string $chatId, @@ -40,7 +42,7 @@ public function __construct( private ?bool $isAnonymous = null, private ?string $type = null, private ?bool $allowsMultipleAnswers = null, - private ?int $correctOptionId = null, + private ?array $correctOptionIds = null, private ?string $explanation = null, private ?string $explanationParseMode = null, private ?array $explanationEntities = null, @@ -53,6 +55,13 @@ public function __construct( private ?ReplyParameters $replyParameters = null, private InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, private ?bool $allowPaidBroadcast = null, + private ?bool $allowsRevoting = null, + private ?bool $shuffleOptions = null, + private ?bool $allowAddingOptions = null, + private ?bool $hideResultsUntilCloses = null, + private ?string $description = null, + private ?string $descriptionParseMode = null, + private ?array $descriptionEntities = null, ) {} public function getHttpMethod(): HttpMethod @@ -87,7 +96,11 @@ public function getData(): array 'is_anonymous' => $this->isAnonymous, 'type' => $this->type, 'allows_multiple_answers' => $this->allowsMultipleAnswers, - 'correct_option_id' => $this->correctOptionId, + 'allows_revoting' => $this->allowsRevoting, + 'shuffle_options' => $this->shuffleOptions, + 'allow_adding_options' => $this->allowAddingOptions, + 'hide_results_until_closes' => $this->hideResultsUntilCloses, + 'correct_option_ids' => $this->correctOptionIds, 'explanation' => $this->explanation, 'explanation_parse_mode' => $this->explanationParseMode, 'explanation_entities' => $this->explanationEntities === null @@ -99,6 +112,14 @@ public function getData(): array 'open_period' => $this->openPeriod, 'close_date' => $this->closeDate?->getTimestamp(), 'is_closed' => $this->isClosed, + 'description' => $this->description, + 'description_parse_mode' => $this->descriptionParseMode, + 'description_entities' => $this->descriptionEntities === null + ? null + : array_map( + static fn(MessageEntity $entity) => $entity->toRequestArray(), + $this->descriptionEntities, + ), 'disable_notification' => $this->disableNotification, 'protect_content' => $this->protectContent, 'allow_paid_broadcast' => $this->allowPaidBroadcast, diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 532ef520..e4c7e1ee 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -52,6 +52,7 @@ use Phptg\BotApi\Method\GetChatMenuButton; use Phptg\BotApi\Method\GetFile; use Phptg\BotApi\Method\GetForumTopicIconStickers; +use Phptg\BotApi\Method\GetManagedBotToken; use Phptg\BotApi\Method\GetMe; use Phptg\BotApi\Method\GetMyCommands; use Phptg\BotApi\Method\GetMyDefaultAdministratorRights; @@ -81,8 +82,9 @@ use Phptg\BotApi\Method\PinChatMessage; use Phptg\BotApi\Method\PostStory; use Phptg\BotApi\Method\PromoteChatMember; -use Phptg\BotApi\Method\RepostStory; use Phptg\BotApi\Method\RemoveBusinessAccountProfilePhoto; +use Phptg\BotApi\Method\ReplaceManagedBotToken; +use Phptg\BotApi\Method\RepostStory; use Phptg\BotApi\Method\RemoveMyProfilePhoto; use Phptg\BotApi\Method\RemoveChatVerification; use Phptg\BotApi\Method\RemoveUserVerification; @@ -90,6 +92,7 @@ use Phptg\BotApi\Method\ReopenGeneralForumTopic; use Phptg\BotApi\Method\RestrictChatMember; use Phptg\BotApi\Method\RevokeChatInviteLink; +use Phptg\BotApi\Method\SavePreparedKeyboardButton; use Phptg\BotApi\Method\SendAnimation; use Phptg\BotApi\Method\SendAudio; use Phptg\BotApi\Method\SendChatAction; @@ -200,6 +203,8 @@ use Phptg\BotApi\Type\Inline\PreparedInlineMessage; use Phptg\BotApi\Type\Inline\SentWebAppMessage; use Phptg\BotApi\Type\InlineKeyboardMarkup; +use Phptg\BotApi\Type\KeyboardButton; +use Phptg\BotApi\Type\PreparedKeyboardButton; use Phptg\BotApi\Type\InputChecklist; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\InputMedia; @@ -1350,6 +1355,14 @@ public function getMe(): FailResult|User return $this->call(new GetMe()); } + /** + * @see https://core.telegram.org/bots/api#getmanagedbottoken + */ + public function getManagedBotToken(int $userId): FailResult|string + { + return $this->call(new GetManagedBotToken($userId)); + } + /** * @see https://core.telegram.org/bots/api#getmycommands */ @@ -1759,6 +1772,14 @@ public function reopenGeneralForumTopic(int|string $chatId): FailResult|true ); } + /** + * @see https://core.telegram.org/bots/api#replacemanagedbottoken + */ + public function replaceManagedBotToken(int $userId): FailResult|string + { + return $this->call(new ReplaceManagedBotToken($userId)); + } + /** * @see https://core.telegram.org/bots/api#replacestickerinset */ @@ -1798,6 +1819,14 @@ public function revokeChatInviteLink(int|string $chatId, string $inviteLink): Fa ); } + /** + * @see https://core.telegram.org/bots/api#savepreparedkeyboardbutton + */ + public function savePreparedKeyboardButton(int $userId, KeyboardButton $button): FailResult|PreparedKeyboardButton + { + return $this->call(new SavePreparedKeyboardButton($userId, $button)); + } + /** * @see https://core.telegram.org/bots/api#savepreparedinlinemessage */ @@ -2476,7 +2505,9 @@ public function sendPhoto( /** * @param InputPollOption[] $options * @param MessageEntity[]|null $questionEntities + * @param int[]|null $correctOptionIds * @param MessageEntity[]|null $explanationEntities + * @param MessageEntity[]|null $descriptionEntities * * @see https://core.telegram.org/bots/api#sendpoll */ @@ -2491,7 +2522,7 @@ public function sendPoll( ?bool $isAnonymous = null, ?string $type = null, ?bool $allowsMultipleAnswers = null, - ?int $correctOptionId = null, + ?array $correctOptionIds = null, ?string $explanation = null, ?string $explanationParseMode = null, ?array $explanationEntities = null, @@ -2504,6 +2535,13 @@ public function sendPoll( ?ReplyParameters $replyParameters = null, InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, ?bool $allowPaidBroadcast = null, + ?bool $allowsRevoting = null, + ?bool $shuffleOptions = null, + ?bool $allowAddingOptions = null, + ?bool $hideResultsUntilCloses = null, + ?string $description = null, + ?string $descriptionParseMode = null, + ?array $descriptionEntities = null, ): FailResult|Message { return $this->call( new SendPoll( @@ -2517,7 +2555,7 @@ public function sendPoll( $isAnonymous, $type, $allowsMultipleAnswers, - $correctOptionId, + $correctOptionIds, $explanation, $explanationParseMode, $explanationEntities, @@ -2530,6 +2568,13 @@ public function sendPoll( $replyParameters, $replyMarkup, $allowPaidBroadcast, + $allowsRevoting, + $shuffleOptions, + $allowAddingOptions, + $hideResultsUntilCloses, + $description, + $descriptionParseMode, + $descriptionEntities, ), ); } diff --git a/src/Type/KeyboardButton.php b/src/Type/KeyboardButton.php index 3e4ce7a3..e15b57f5 100644 --- a/src/Type/KeyboardButton.php +++ b/src/Type/KeyboardButton.php @@ -21,6 +21,7 @@ public function __construct( public ?WebAppInfo $webApp = null, public ?string $iconCustomEmojiId = null, public ?string $style = null, + public ?KeyboardButtonRequestManagedBot $requestManagedBot = null, ) {} public function toRequestArray(): array @@ -32,6 +33,7 @@ public function toRequestArray(): array 'style' => $this->style, 'request_users' => $this->requestUsers?->toRequestArray(), 'request_chat' => $this->requestChat?->toRequestArray(), + 'request_managed_bot' => $this->requestManagedBot?->toRequestArray(), 'request_contact' => $this->requestContact, 'request_location' => $this->requestLocation, 'request_poll' => $this->requestPoll?->toRequestArray(), diff --git a/src/Type/KeyboardButtonRequestManagedBot.php b/src/Type/KeyboardButtonRequestManagedBot.php new file mode 100644 index 00000000..02a372b6 --- /dev/null +++ b/src/Type/KeyboardButtonRequestManagedBot.php @@ -0,0 +1,31 @@ + $this->requestId, + 'suggested_name' => $this->suggestedName, + 'suggested_username' => $this->suggestedUsername, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/src/Type/ManagedBotCreated.php b/src/Type/ManagedBotCreated.php new file mode 100644 index 00000000..66b8f9b3 --- /dev/null +++ b/src/Type/ManagedBotCreated.php @@ -0,0 +1,17 @@ + $this->quotePosition, 'checklist_task_id' => $this->checklistTaskId, + 'poll_option_id' => $this->pollOptionId, ], static fn(mixed $value): bool => $value !== null, ); diff --git a/src/Type/Update/Update.php b/src/Type/Update/Update.php index 4c649918..8feb5952 100644 --- a/src/Type/Update/Update.php +++ b/src/Type/Update/Update.php @@ -19,6 +19,7 @@ use Phptg\BotApi\Type\ChatMemberUpdated; use Phptg\BotApi\Type\Inline\ChosenInlineResult; use Phptg\BotApi\Type\Inline\InlineQuery; +use Phptg\BotApi\Type\ManagedBotUpdated; use Phptg\BotApi\Type\Message; use Phptg\BotApi\Type\MessageReactionCountUpdated; use Phptg\BotApi\Type\MessageReactionUpdated; @@ -63,6 +64,7 @@ public function __construct( public readonly ?ChatBoostUpdated $chatBoost = null, public readonly ?ChatBoostRemoved $removedChatBoost = null, public readonly ?PaidMediaPurchased $purchasedPaidMedia = null, + public readonly ?ManagedBotUpdated $managedBot = null, ) {} /** diff --git a/src/Type/User.php b/src/Type/User.php index d4cdcc6b..a46ae50a 100644 --- a/src/Type/User.php +++ b/src/Type/User.php @@ -27,6 +27,7 @@ public function __construct( public ?bool $hasMainWebApp = null, public ?bool $hasTopicsEnabled = null, public ?bool $allowsUsersToCreateTopics = null, + public ?bool $canManageBots = null, ) {} public function toRequestArray(): array @@ -48,6 +49,7 @@ public function toRequestArray(): array 'has_main_web_app' => $this->hasMainWebApp, 'has_topics_enabled' => $this->hasTopicsEnabled, 'allows_users_to_create_topics' => $this->allowsUsersToCreateTopics, + 'can_manage_bots' => $this->canManageBots, ], static fn(mixed $value): bool => $value !== null, ); diff --git a/tests/Method/GetManagedBotTokenTest.php b/tests/Method/GetManagedBotTokenTest.php new file mode 100644 index 00000000..25c52ffe --- /dev/null +++ b/tests/Method/GetManagedBotTokenTest.php @@ -0,0 +1,38 @@ +getHttpMethod()); + assertSame('getManagedBotToken', $method->getApiMethod()); + assertSame( + [ + 'user_id' => 123, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new GetManagedBotToken(123); + + $preparedResult = TestHelper::createSuccessStubApi('123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11')->call($method); + + assertSame('123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', $preparedResult); + } +} diff --git a/tests/Method/ReplaceManagedBotTokenTest.php b/tests/Method/ReplaceManagedBotTokenTest.php new file mode 100644 index 00000000..89982f99 --- /dev/null +++ b/tests/Method/ReplaceManagedBotTokenTest.php @@ -0,0 +1,38 @@ +getHttpMethod()); + assertSame('replaceManagedBotToken', $method->getApiMethod()); + assertSame( + [ + 'user_id' => 123, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new ReplaceManagedBotToken(123); + + $preparedResult = TestHelper::createSuccessStubApi('789012:XYZ-abc3456defGhi-lmn78O9p2q456rs22')->call($method); + + assertSame('789012:XYZ-abc3456defGhi-lmn78O9p2q456rs22', $preparedResult); + } +} diff --git a/tests/Method/SavePreparedKeyboardButtonTest.php b/tests/Method/SavePreparedKeyboardButtonTest.php new file mode 100644 index 00000000..ba8eeff9 --- /dev/null +++ b/tests/Method/SavePreparedKeyboardButtonTest.php @@ -0,0 +1,49 @@ +getHttpMethod()); + assertSame('savePreparedKeyboardButton', $method->getApiMethod()); + assertSame( + [ + 'user_id' => 123, + 'button' => [ + 'text' => 'Test Button', + ], + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $button = new KeyboardButton('Test Button'); + $method = new SavePreparedKeyboardButton(123, $button); + + $preparedResult = TestHelper::createSuccessStubApi([ + 'id' => 'prepared_btn_789', + ])->call($method); + + assertInstanceOf(PreparedKeyboardButton::class, $preparedResult); + assertSame('prepared_btn_789', $preparedResult->id); + } +} diff --git a/tests/Method/SendPollTest.php b/tests/Method/SendPollTest.php index af19b264..bf0e555b 100644 --- a/tests/Method/SendPollTest.php +++ b/tests/Method/SendPollTest.php @@ -45,6 +45,7 @@ public function testFull(): void $option2 = new InputPollOption('Bad'); $messageEntity1 = new MessageEntity('bold', 0, 5); $messageEntity2 = new MessageEntity('bold', 1, 3); + $messageEntity3 = new MessageEntity('italic', 2, 4); $date = new DateTimeImmutable(); $replyParameters = new ReplyParameters(23); $replyMarkup = new ForceReply(); @@ -59,7 +60,7 @@ public function testFull(): void true, 'quiz', false, - 23, + [0, 1], 'Good explanation', 'Markdown', [$messageEntity2], @@ -72,6 +73,13 @@ public function testFull(): void $replyParameters, $replyMarkup, true, + true, + true, + true, + true, + 'Poll description', + 'HTML', + [$messageEntity3], ); assertSame(HttpMethod::POST, $method->getHttpMethod()); @@ -91,13 +99,20 @@ public function testFull(): void 'is_anonymous' => true, 'type' => 'quiz', 'allows_multiple_answers' => false, - 'correct_option_id' => 23, + 'allows_revoting' => true, + 'shuffle_options' => true, + 'allow_adding_options' => true, + 'hide_results_until_closes' => true, + 'correct_option_ids' => [0, 1], 'explanation' => 'Good explanation', 'explanation_parse_mode' => 'Markdown', 'explanation_entities' => [$messageEntity2->toRequestArray()], 'open_period' => 300, 'close_date' => $date->getTimestamp(), 'is_closed' => true, + 'description' => 'Poll description', + 'description_parse_mode' => 'HTML', + 'description_entities' => [$messageEntity3->toRequestArray()], 'disable_notification' => false, 'protect_content' => false, 'allow_paid_broadcast' => true, diff --git a/tests/Method/UpdatingMessage/StopPollTest.php b/tests/Method/UpdatingMessage/StopPollTest.php index 82560aa9..9ce26e8e 100644 --- a/tests/Method/UpdatingMessage/StopPollTest.php +++ b/tests/Method/UpdatingMessage/StopPollTest.php @@ -54,13 +54,14 @@ public function testPrepareResult(): void 'id' => '12', 'question' => 'Why?', 'options' => [ - ['text' => 'One', 'voter_count' => 12], + ['persistent_id' => 'pid1', 'text' => 'One', 'voter_count' => 12], ], 'total_voter_count' => 42, 'is_closed' => true, 'is_anonymous' => false, 'type' => 'regular', 'allows_multiple_answers' => true, + 'allows_revoting' => false, ])->call($method); assertSame('12', $preparedResult->id); diff --git a/tests/TelegramBotApi/TelegramBotApiTest.php b/tests/TelegramBotApi/TelegramBotApiTest.php index b057d3c9..662d513b 100644 --- a/tests/TelegramBotApi/TelegramBotApiTest.php +++ b/tests/TelegramBotApi/TelegramBotApiTest.php @@ -25,6 +25,7 @@ use Phptg\BotApi\Type\ChatPermissions; use Phptg\BotApi\Type\File; use Phptg\BotApi\Type\ForumTopic; +use Phptg\BotApi\Type\KeyboardButton; use Phptg\BotApi\Type\Game\GameHighScore; use Phptg\BotApi\Type\Inline\InlineQueryResultContact; use Phptg\BotApi\Type\Inline\InlineQueryResultGame; @@ -41,6 +42,7 @@ use Phptg\BotApi\Type\MessageId; use Phptg\BotApi\Type\OwnedGifts; use Phptg\BotApi\Type\Payment\StarTransactions; +use Phptg\BotApi\Type\PreparedKeyboardButton; use Phptg\BotApi\Type\StarAmount; use Phptg\BotApi\Type\Sticker\Gifts; use Phptg\BotApi\Type\Sticker\InputSticker; @@ -1257,6 +1259,15 @@ public function testGetMe(): void assertSame(1, $result->id); } + public function testGetManagedBotToken(): void + { + $api = TestHelper::createSuccessStubApi('123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11'); + + $result = $api->getManagedBotToken(789); + + assertSame('123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', $result); + } + public function testGetMyCommands(): void { $api = TestHelper::createSuccessStubApi([ @@ -1731,6 +1742,15 @@ public function testReopenGeneralForumTopic(): void assertTrue($result); } + public function testReplaceManagedBotToken(): void + { + $api = TestHelper::createSuccessStubApi('789012:XYZ-abc3456defGhi-lmn78O9p2q456rs22'); + + $result = $api->replaceManagedBotToken(789); + + assertSame('789012:XYZ-abc3456defGhi-lmn78O9p2q456rs22', $result); + } + public function testReplaceStickerInSet(): void { $api = TestHelper::createSuccessStubApi(true); @@ -1774,6 +1794,19 @@ public function testRevokeChatInviteLink(): void assertSame(23, $result->creator->id); } + public function testSavePreparedKeyboardButton(): void + { + $api = TestHelper::createSuccessStubApi([ + 'id' => 'prepared_btn_123', + ]); + + $button = new KeyboardButton('Test Button'); + $result = $api->savePreparedKeyboardButton(456, $button); + + assertInstanceOf(PreparedKeyboardButton::class, $result); + assertSame('prepared_btn_123', $result->id); + } + public function testSavePreparedInlineMessage(): void { $api = TestHelper::createSuccessStubApi([ @@ -2458,13 +2491,14 @@ public function testStopPoll(): void 'id' => '12', 'question' => 'Why?', 'options' => [ - ['text' => 'One', 'voter_count' => 12], + ['persistent_id' => 'pid1', 'text' => 'One', 'voter_count' => 12], ], 'total_voter_count' => 42, 'is_closed' => true, 'is_anonymous' => false, 'type' => 'regular', 'allows_multiple_answers' => true, + 'allows_revoting' => false, ]); $result = $api->stopPoll(1, 2); diff --git a/tests/Type/ExternalReplyInfoTest.php b/tests/Type/ExternalReplyInfoTest.php index 71b6aec1..b6a79694 100644 --- a/tests/Type/ExternalReplyInfoTest.php +++ b/tests/Type/ExternalReplyInfoTest.php @@ -182,6 +182,7 @@ public function testFromTelegramResult(): void 'is_anonymous' => false, 'type' => 'regular', 'allows_multiple_answers' => true, + 'allows_revoting' => false, ], 'venue' => [ 'location' => [ diff --git a/tests/Type/KeyboardButtonRequestManagedBotTest.php b/tests/Type/KeyboardButtonRequestManagedBotTest.php new file mode 100644 index 00000000..a41525ce --- /dev/null +++ b/tests/Type/KeyboardButtonRequestManagedBotTest.php @@ -0,0 +1,52 @@ +requestId); + assertNull($keyboardButtonRequestManagedBot->suggestedName); + assertNull($keyboardButtonRequestManagedBot->suggestedUsername); + + assertSame( + [ + 'request_id' => 1, + ], + $keyboardButtonRequestManagedBot->toRequestArray(), + ); + } + + public function testFilled(): void + { + $keyboardButtonRequestManagedBot = new KeyboardButtonRequestManagedBot( + 1, + 'My Bot', + 'my_bot', + ); + + assertSame(1, $keyboardButtonRequestManagedBot->requestId); + assertSame('My Bot', $keyboardButtonRequestManagedBot->suggestedName); + assertSame('my_bot', $keyboardButtonRequestManagedBot->suggestedUsername); + + assertSame( + [ + 'request_id' => 1, + 'suggested_name' => 'My Bot', + 'suggested_username' => 'my_bot', + ], + $keyboardButtonRequestManagedBot->toRequestArray(), + ); + } +} diff --git a/tests/Type/KeyboardButtonTest.php b/tests/Type/KeyboardButtonTest.php index a0404803..534da793 100644 --- a/tests/Type/KeyboardButtonTest.php +++ b/tests/Type/KeyboardButtonTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Type\KeyboardButton; use Phptg\BotApi\Type\KeyboardButtonPollType; use Phptg\BotApi\Type\KeyboardButtonRequestChat; +use Phptg\BotApi\Type\KeyboardButtonRequestManagedBot; use Phptg\BotApi\Type\KeyboardButtonRequestUsers; use Phptg\BotApi\Type\WebAppInfo; @@ -27,6 +28,7 @@ public function testBase(): void assertNull($button->style); assertNull($button->requestUsers); assertNull($button->requestChat); + assertNull($button->requestManagedBot); assertNull($button->requestContact); assertNull($button->requestLocation); assertNull($button->requestPoll); @@ -44,6 +46,7 @@ public function testFilled(): void { $requestUsers = new KeyboardButtonRequestUsers(1); $requestChat = new KeyboardButtonRequestChat(2, true); + $requestManagedBot = new KeyboardButtonRequestManagedBot(3); $requestPoll = new KeyboardButtonPollType('test'); $webApp = new WebAppInfo('https://example.com/test'); $button = new KeyboardButton( @@ -56,6 +59,7 @@ public function testFilled(): void $webApp, '5368324170671202286', 'primary', + $requestManagedBot, ); assertSame('test', $button->text); @@ -63,6 +67,7 @@ public function testFilled(): void assertSame('primary', $button->style); assertSame($requestUsers, $button->requestUsers); assertSame($requestChat, $button->requestChat); + assertSame($requestManagedBot, $button->requestManagedBot); assertTrue($button->requestContact); assertFalse($button->requestLocation); assertSame($requestPoll, $button->requestPoll); @@ -75,6 +80,7 @@ public function testFilled(): void 'style' => 'primary', 'request_users' => $requestUsers->toRequestArray(), 'request_chat' => $requestChat->toRequestArray(), + 'request_managed_bot' => $requestManagedBot->toRequestArray(), 'request_contact' => true, 'request_location' => false, 'request_poll' => $requestPoll->toRequestArray(), diff --git a/tests/Type/ManagedBotCreatedTest.php b/tests/Type/ManagedBotCreatedTest.php new file mode 100644 index 00000000..9b34dbff --- /dev/null +++ b/tests/Type/ManagedBotCreatedTest.php @@ -0,0 +1,43 @@ +bot); + } + + public function testFromTelegramResult(): void + { + $managedBotCreated = (new ObjectFactory())->create([ + 'bot' => [ + 'id' => 1, + 'is_bot' => true, + 'first_name' => 'TestBot', + 'last_name' => 'Bot', + ], + ], null, ManagedBotCreated::class); + + assertInstanceOf(ManagedBotCreated::class, $managedBotCreated); + assertInstanceOf(User::class, $managedBotCreated->bot); + assertSame(1, $managedBotCreated->bot->id); + assertSame(true, $managedBotCreated->bot->isBot); + assertSame('TestBot', $managedBotCreated->bot->firstName); + assertSame('Bot', $managedBotCreated->bot->lastName); + } +} diff --git a/tests/Type/ManagedBotUpdatedTest.php b/tests/Type/ManagedBotUpdatedTest.php new file mode 100644 index 00000000..f2d6d69c --- /dev/null +++ b/tests/Type/ManagedBotUpdatedTest.php @@ -0,0 +1,56 @@ +user); + assertSame($bot, $managedBotUpdated->bot); + } + + public function testFromTelegramResult(): void + { + $managedBotUpdated = (new ObjectFactory())->create([ + 'user' => [ + 'id' => 1, + 'is_bot' => false, + 'first_name' => 'John', + 'last_name' => 'Doe', + ], + 'bot' => [ + 'id' => 2, + 'is_bot' => true, + 'first_name' => 'TestBot', + 'username' => 'test_bot', + ], + ], null, ManagedBotUpdated::class); + + assertInstanceOf(ManagedBotUpdated::class, $managedBotUpdated); + assertInstanceOf(User::class, $managedBotUpdated->user); + assertSame(1, $managedBotUpdated->user->id); + assertSame(false, $managedBotUpdated->user->isBot); + assertSame('John', $managedBotUpdated->user->firstName); + assertSame('Doe', $managedBotUpdated->user->lastName); + assertInstanceOf(User::class, $managedBotUpdated->bot); + assertSame(2, $managedBotUpdated->bot->id); + assertSame(true, $managedBotUpdated->bot->isBot); + assertSame('TestBot', $managedBotUpdated->bot->firstName); + assertSame('test_bot', $managedBotUpdated->bot->username); + } +} diff --git a/tests/Type/MessageTest.php b/tests/Type/MessageTest.php index 3e9b24ef..7d7e3699 100644 --- a/tests/Type/MessageTest.php +++ b/tests/Type/MessageTest.php @@ -137,6 +137,7 @@ public function testBase(): void assertNull($message->checklistTasksDone); assertNull($message->checklistTasksAdded); assertNull($message->replyToChecklistTaskId); + assertNull($message->replyToPollOptionId); assertNull($message->directMessagesTopic); assertNull($message->suggestedPostInfo); assertNull($message->suggestedPostApproved); @@ -146,6 +147,9 @@ public function testBase(): void assertNull($message->suggestedPostRefunded); assertNull($message->chatOwnerLeft); assertNull($message->chatOwnerChanged); + assertNull($message->managedBotCreated); + assertNull($message->pollOptionAdded); + assertNull($message->pollOptionDeleted); } public function testFromTelegramResult(): void @@ -212,6 +216,7 @@ public function testFromTelegramResult(): void 'id' => 8863, ], 'reply_to_checklist_task_id' => 789, + 'reply_to_poll_option_id' => 'pid1', 'via_bot' => [ 'id' => 127, 'is_bot' => false, @@ -334,6 +339,7 @@ public function testFromTelegramResult(): void 'is_anonymous' => false, 'type' => 'regular', 'allows_multiple_answers' => true, + 'allows_revoting' => false, ], 'venue' => [ 'location' => [ @@ -651,6 +657,21 @@ public function testFromTelegramResult(): void 'first_name' => 'Ivan', ], ], + 'managed_bot_created' => [ + 'bot' => [ + 'id' => 802, + 'is_bot' => true, + 'first_name' => 'ManagedBot', + ], + ], + 'poll_option_added' => [ + 'option_persistent_id' => 'pid1', + 'option_text' => 'New option', + ], + 'poll_option_deleted' => [ + 'option_persistent_id' => 'pid2', + 'option_text' => 'Deleted option', + ], ], null, Message::class); assertSame(7, $message->messageId); @@ -780,6 +801,7 @@ public function testFromTelegramResult(): void assertInstanceOf(ChecklistTasksAdded::class, $message->checklistTasksAdded); assertCount(2, $message->checklistTasksAdded->tasks); assertSame(789, $message->replyToChecklistTaskId); + assertSame('pid1', $message->replyToPollOptionId); assertSame(12345, $message->directMessagesTopic?->topicId); assertSame('pending', $message->suggestedPostInfo?->state); assertSame('XTR', $message->suggestedPostInfo?->price?->currency); @@ -792,5 +814,11 @@ public function testFromTelegramResult(): void assertSame('refund_reason', $message->suggestedPostRefunded?->reason); assertSame(800, $message->chatOwnerLeft?->newOwner?->id); assertSame(801, $message->chatOwnerChanged?->newOwner->id); + assertSame(802, $message->managedBotCreated?->bot->id); + assertSame('ManagedBot', $message->managedBotCreated?->bot->firstName); + assertSame('pid1', $message->pollOptionAdded?->optionPersistentId); + assertSame('New option', $message->pollOptionAdded?->optionText); + assertSame('pid2', $message->pollOptionDeleted?->optionPersistentId); + assertSame('Deleted option', $message->pollOptionDeleted?->optionText); } } diff --git a/tests/Type/PollAnswerTest.php b/tests/Type/PollAnswerTest.php index a425b32c..522c33c4 100644 --- a/tests/Type/PollAnswerTest.php +++ b/tests/Type/PollAnswerTest.php @@ -15,10 +15,11 @@ final class PollAnswerTest extends TestCase { public function testBase(): void { - $answer = new PollAnswer('sadg', [2, 4]); + $answer = new PollAnswer('sadg', [2, 4], ['pid2', 'pid4']); assertSame('sadg', $answer->pollId); assertSame([2, 4], $answer->optionIds); + assertSame(['pid2', 'pid4'], $answer->optionPersistentIds); assertNull($answer->voterChat); assertNull($answer->user); } @@ -28,6 +29,7 @@ public function testFromTelegramResult(): void $answer = (new ObjectFactory())->create([ 'poll_id' => 'sadg', 'option_ids' => [2, 4], + 'option_persistent_ids' => ['pid2', 'pid4'], 'voter_chat' => [ 'id' => 42, 'type' => 'private', @@ -41,6 +43,7 @@ public function testFromTelegramResult(): void assertSame('sadg', $answer->pollId); assertSame([2, 4], $answer->optionIds); + assertSame(['pid2', 'pid4'], $answer->optionPersistentIds); assertSame(42, $answer->voterChat?->id); assertSame(43, $answer->user?->id); } diff --git a/tests/Type/PollOptionAddedTest.php b/tests/Type/PollOptionAddedTest.php new file mode 100644 index 00000000..cedc2a59 --- /dev/null +++ b/tests/Type/PollOptionAddedTest.php @@ -0,0 +1,56 @@ +optionPersistentId); + assertSame('Option text', $pollOptionAdded->optionText); + assertNull($pollOptionAdded->pollMessage); + assertNull($pollOptionAdded->optionTextEntities); + } + + public function testFromTelegramResult(): void + { + $pollOptionAdded = (new ObjectFactory())->create([ + 'option_persistent_id' => 'pid1', + 'option_text' => 'Option text', + 'poll_message' => [ + 'message_id' => 1, + 'date' => 1620000000, + 'chat' => ['id' => 10, 'type' => 'private'], + ], + 'option_text_entities' => [ + [ + 'type' => 'bold', + 'offset' => 0, + 'length' => 6, + ], + ], + ], null, PollOptionAdded::class); + + assertInstanceOf(PollOptionAdded::class, $pollOptionAdded); + assertSame('pid1', $pollOptionAdded->optionPersistentId); + assertSame('Option text', $pollOptionAdded->optionText); + assertInstanceOf(Message::class, $pollOptionAdded->pollMessage); + assertSame(1, $pollOptionAdded->pollMessage->messageId); + assertCount(1, $pollOptionAdded->optionTextEntities); + assertSame('bold', $pollOptionAdded->optionTextEntities[0]->type); + } +} diff --git a/tests/Type/PollOptionDeletedTest.php b/tests/Type/PollOptionDeletedTest.php new file mode 100644 index 00000000..0443420c --- /dev/null +++ b/tests/Type/PollOptionDeletedTest.php @@ -0,0 +1,56 @@ +optionPersistentId); + assertSame('Option text', $pollOptionDeleted->optionText); + assertNull($pollOptionDeleted->pollMessage); + assertNull($pollOptionDeleted->optionTextEntities); + } + + public function testFromTelegramResult(): void + { + $pollOptionDeleted = (new ObjectFactory())->create([ + 'option_persistent_id' => 'pid1', + 'option_text' => 'Option text', + 'poll_message' => [ + 'message_id' => 1, + 'date' => 1620000000, + 'chat' => ['id' => 10, 'type' => 'private'], + ], + 'option_text_entities' => [ + [ + 'type' => 'bold', + 'offset' => 0, + 'length' => 6, + ], + ], + ], null, PollOptionDeleted::class); + + assertInstanceOf(PollOptionDeleted::class, $pollOptionDeleted); + assertSame('pid1', $pollOptionDeleted->optionPersistentId); + assertSame('Option text', $pollOptionDeleted->optionText); + assertInstanceOf(Message::class, $pollOptionDeleted->pollMessage); + assertSame(1, $pollOptionDeleted->pollMessage->messageId); + assertCount(1, $pollOptionDeleted->optionTextEntities); + assertSame('bold', $pollOptionDeleted->optionTextEntities[0]->type); + } +} diff --git a/tests/Type/PollOptionTest.php b/tests/Type/PollOptionTest.php index ce99470c..0e31244c 100644 --- a/tests/Type/PollOptionTest.php +++ b/tests/Type/PollOptionTest.php @@ -16,16 +16,21 @@ final class PollOptionTest extends TestCase { public function testBase(): void { - $option = new PollOption('A', 25); + $option = new PollOption('pid1', 'A', 25); + assertSame('pid1', $option->persistentId); assertSame('A', $option->text); assertSame(25, $option->voterCount); assertNull($option->textEntities); + assertNull($option->addedByUser); + assertNull($option->addedByChat); + assertNull($option->additionDate); } public function testFromTelegramResult(): void { $option = (new ObjectFactory())->create([ + 'persistent_id' => 'pid1', 'text' => 'A', 'voter_count' => 25, 'text_entities' => [ @@ -35,12 +40,27 @@ public function testFromTelegramResult(): void 'type' => 'bold', ], ], + 'added_by_user' => [ + 'id' => 42, + 'is_bot' => false, + 'first_name' => 'John', + ], + 'added_by_chat' => [ + 'id' => 100, + 'type' => 'group', + ], + 'addition_date' => 1700000000, ], null, PollOption::class); + assertSame('pid1', $option->persistentId); assertSame('A', $option->text); assertSame(25, $option->voterCount); assertCount(1, $option->textEntities); assertSame(23, $option->textEntities[0]->offset); + + assertSame(42, $option->addedByUser->id); + assertSame(100, $option->addedByChat->id); + assertSame(1700000000, $option->additionDate); } } diff --git a/tests/Type/PollTest.php b/tests/Type/PollTest.php index c9602e7d..df3f5b40 100644 --- a/tests/Type/PollTest.php +++ b/tests/Type/PollTest.php @@ -19,7 +19,7 @@ final class PollTest extends TestCase { public function testBase(): void { - $option = new PollOption('One', 12); + $option = new PollOption('pid1', 'One', 12); $poll = new Poll( '12', 'Why?', @@ -29,6 +29,7 @@ public function testBase(): void false, 'regular', true, + false, ); assertSame('12', $poll->id); @@ -39,11 +40,14 @@ public function testBase(): void assertFalse($poll->isAnonymous); assertSame('regular', $poll->type); assertTrue($poll->allowsMultipleAnswers); - assertNull($poll->correctOptionId); + assertFalse($poll->allowsRevoting); + assertNull($poll->correctOptionIds); assertNull($poll->explanation); assertNull($poll->explanationEntities); assertNull($poll->openPeriod); assertNull($poll->closeDate); + assertNull($poll->description); + assertNull($poll->descriptionEntities); } public function testFromTelegramResult(): void @@ -52,13 +56,14 @@ public function testFromTelegramResult(): void 'id' => '12', 'question' => 'Why?', 'options' => [ - ['text' => 'One', 'voter_count' => 12], + ['persistent_id' => 'pid1', 'text' => 'One', 'voter_count' => 12], ], 'total_voter_count' => 42, 'is_closed' => true, 'is_anonymous' => false, 'type' => 'regular', 'allows_multiple_answers' => true, + 'allows_revoting' => true, 'question_entities' => [ [ 'offset' => 0, @@ -66,7 +71,7 @@ public function testFromTelegramResult(): void 'type' => 'bold', ], ], - 'correct_option_id' => 23, + 'correct_option_ids' => [0, 2], 'explanation' => 'Because', 'explanation_entities' => [ [ @@ -77,6 +82,14 @@ public function testFromTelegramResult(): void ], 'open_period' => 123, 'close_date' => 456, + 'description' => 'Poll description', + 'description_entities' => [ + [ + 'offset' => 0, + 'length' => 4, + 'type' => 'bold', + ], + ], ], null, Poll::class); assertSame('12', $poll->id); @@ -90,11 +103,12 @@ public function testFromTelegramResult(): void assertFalse($poll->isAnonymous); assertSame('regular', $poll->type); assertTrue($poll->allowsMultipleAnswers); + assertTrue($poll->allowsRevoting); assertCount(1, $poll->questionEntities); assertSame(35, $poll->questionEntities[0]->length); - assertSame(23, $poll->correctOptionId); + assertSame([0, 2], $poll->correctOptionIds); assertSame('Because', $poll->explanation); assertCount(1, $poll->explanationEntities); @@ -102,5 +116,8 @@ public function testFromTelegramResult(): void assertSame(123, $poll->openPeriod); assertSame(456, $poll->closeDate); + assertSame('Poll description', $poll->description); + assertCount(1, $poll->descriptionEntities); + assertSame(4, $poll->descriptionEntities[0]->length); } } diff --git a/tests/Type/PreparedKeyboardButtonTest.php b/tests/Type/PreparedKeyboardButtonTest.php new file mode 100644 index 00000000..ba612911 --- /dev/null +++ b/tests/Type/PreparedKeyboardButtonTest.php @@ -0,0 +1,32 @@ +id); + } + + public function testFromTelegramResult(): void + { + $button = (new ObjectFactory())->create([ + 'id' => 'button_456', + ], null, PreparedKeyboardButton::class); + + assertInstanceOf(PreparedKeyboardButton::class, $button); + assertSame('button_456', $button->id); + } +} diff --git a/tests/Type/ReplyParametersTest.php b/tests/Type/ReplyParametersTest.php index efa63e46..4a333a4c 100644 --- a/tests/Type/ReplyParametersTest.php +++ b/tests/Type/ReplyParametersTest.php @@ -26,6 +26,7 @@ public function testBase(): void assertNull($replyParameters->quoteEntities); assertNull($replyParameters->quotePosition); assertNull($replyParameters->checklistTaskId); + assertNull($replyParameters->pollOptionId); assertSame( [ @@ -47,6 +48,7 @@ public function testFilled(): void [$quoteEntity], 23, 456, + 'pid1', ); assertSame(99, $replyParameters->messageId); @@ -57,6 +59,7 @@ public function testFilled(): void assertSame([$quoteEntity], $replyParameters->quoteEntities); assertSame(23, $replyParameters->quotePosition); assertSame(456, $replyParameters->checklistTaskId); + assertSame('pid1', $replyParameters->pollOptionId); assertSame( [ @@ -68,6 +71,7 @@ public function testFilled(): void 'quote_entities' => [$quoteEntity->toRequestArray()], 'quote_position' => 23, 'checklist_task_id' => 456, + 'poll_option_id' => 'pid1', ], $replyParameters->toRequestArray(), ); diff --git a/tests/Type/Update/UpdateTest.php b/tests/Type/Update/UpdateTest.php index 9d887696..56b2754f 100644 --- a/tests/Type/Update/UpdateTest.php +++ b/tests/Type/Update/UpdateTest.php @@ -46,6 +46,7 @@ public function testBase(): void assertNull($update->chatBoost); assertNull($update->removedChatBoost); assertNull($update->purchasedPaidMedia); + assertNull($update->managedBot); assertNull($update->getRaw()); assertNull($update->getRaw(true)); } @@ -218,18 +219,20 @@ public function testFromTelegramResult(): void 'id' => 'poll1', 'question' => 'Question', 'options' => [ - ['text' => 'Option 1', 'voter_count' => 7], - ['text' => 'Option 2', 'voter_count' => 5], + ['persistent_id' => 'pid1', 'text' => 'Option 1', 'voter_count' => 7], + ['persistent_id' => 'pid2', 'text' => 'Option 2', 'voter_count' => 5], ], 'total_voter_count' => 12, 'is_closed' => true, 'is_anonymous' => false, 'type' => 'regular', 'allows_multiple_answers' => false, + 'allows_revoting' => true, ], 'poll_answer' => [ 'poll_id' => 'poll2', 'option_ids' => [0], + 'option_persistent_ids' => ['pid1'], ], 'my_chat_member' => [ 'chat' => [ @@ -335,6 +338,18 @@ public function testFromTelegramResult(): void ], ], ], + 'managed_bot' => [ + 'user' => [ + 'id' => 123, + 'is_bot' => false, + 'first_name' => 'Creator', + ], + 'bot' => [ + 'id' => 456, + 'is_bot' => true, + 'first_name' => 'ManagedBot', + ], + ], ]; $update = (new ObjectFactory())->create($data, null, Update::class); @@ -362,6 +377,8 @@ public function testFromTelegramResult(): void assertSame(23682, $update->chatBoost?->chat->id); assertSame(1735, $update->removedChatBoost?->chat->id); assertSame(1235, $update->purchasedPaidMedia->from->id); + assertSame(123, $update->managedBot?->user->id); + assertSame(456, $update->managedBot?->bot->id); assertNull($update->getRaw()); assertNull($update->getRaw(true)); } diff --git a/tests/Type/UserTest.php b/tests/Type/UserTest.php index 87939a05..e6e3c3c4 100644 --- a/tests/Type/UserTest.php +++ b/tests/Type/UserTest.php @@ -33,6 +33,7 @@ public function testBase(): void assertNull($user->hasMainWebApp); assertNull($user->hasTopicsEnabled); assertNull($user->allowsUsersToCreateTopics); + assertNull($user->canManageBots); } public function testToRequestArray(): void @@ -53,6 +54,7 @@ public function testToRequestArray(): void false, true, true, + true, ); assertSame( @@ -72,6 +74,7 @@ public function testToRequestArray(): void 'has_main_web_app' => false, 'has_topics_enabled' => true, 'allows_users_to_create_topics' => true, + 'can_manage_bots' => true, ], $user->toRequestArray(), ); @@ -95,6 +98,7 @@ public function testFromTelegramResult(): void 'has_main_web_app' => false, 'has_topics_enabled' => true, 'allows_users_to_create_topics' => true, + 'can_manage_bots' => true, ], null, User::class); assertInstanceOf(User::class, $user); @@ -113,5 +117,6 @@ public function testFromTelegramResult(): void assertSame(false, $user->hasMainWebApp); assertSame(true, $user->hasTopicsEnabled); assertSame(true, $user->allowsUsersToCreateTopics); + assertSame(true, $user->canManageBots); } }