diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..167d8d2f --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,5 @@ +# Agent Guidelines + +## Writing Tests + +- For `InputFile|string` parameters, the string must be a file ID, not a URL. diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d5ca01d..819a78f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Telegram Bot API for PHP Change Log +## 0.21 May 9, 2026 + +- New #199: Add `supportsGuestQueries` field to `User` type. +- New #199: Add `guestQueryId`, `guestBotCallerUser` and `guestBotCallerChat` fields to `Message` type. +- New #199: Add `guestMessage` field to `Update` type. +- New #199: Add `SentGuestMessage` type. +- New #199: Add `AnswerGuestQuery` method. +- New #199: Add `canReactToMessages` field to `ChatMemberRestricted` and `ChatPermissions` types. +- New #199: Add `returnBots` parameter to `GetChatAdministrators` method. +- New #199: Add `DeleteAllMessageReactions` method. +- New #199: Add `DeleteMessageReaction` method. +- New #199: Add `InputMediaSticker`, `InputMediaVenue`, `InputMediaLocation` and `InputMediaLivePhoto` types. +- New #199: Add `PollMedia` and `LivePhoto` type. +- New #199: Add `explanationMedia`, `media`, `membersOnly` and `countryCodes` fields to `Poll` type. +- New #199: Add `media` field to `PollOption` type. +- New #199: Add `InputPollMedia` and `InputPollOptionMedia` types. +- New #199: Add `live_photo` field to `Message` and `ExternalReplyInfo` types. +- New #199: Add `SendLivePhoto` method. +- New #199: Add `PaidMediaLivePhoto` type. +- New #199: Add `InputPaidMediaLivePhoto` type. +- New #199: Add `explanationMedia`, `media`, `membersOnly` and `countryCodes` parameters to `SendPoll` method. +- New #199: Add `media` field to `InputPollOption` type. +- Enh #199: `SendMediaGroup` method now supports `InputMediaLivePhoto` type. +- New #199: Add `BotAccessSettings` type. +- New #199: Add `GetManagedBotAccessSettings` method. +- New #199: Add `SetManagedBotAccessSettings` method. +- New #199: Add `GetUserPersonalChatMessages` method. +- Chg #199: `text` parameter in `SendMessageDraft` method is now optional. + ## 0.20 April 26, 2026 - Chg #197: `$data` parameter type in `TransportInterface::postWithFiles()` changed from `array` diff --git a/README.md b/README.md index 85ba2438..52ec9e79 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.6 (April 3, 2026) is **fully supported**. +✔️ Telegram Bot API 10.0 (May 8, 2026) is **fully supported**. ♻️ **Zero dependencies** — no third-party libraries, only native PHP. diff --git a/src/Method/DeleteAllMessageReactions.php b/src/Method/DeleteAllMessageReactions.php new file mode 100644 index 00000000..374fbfb3 --- /dev/null +++ b/src/Method/DeleteAllMessageReactions.php @@ -0,0 +1,50 @@ + + */ +final readonly class DeleteAllMessageReactions implements MethodInterface +{ + public function __construct( + private int|string $chatId, + private ?int $userId = null, + private ?int $actorChatId = null, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'deleteAllMessageReactions'; + } + + public function getData(): array + { + return array_filter( + [ + 'chat_id' => $this->chatId, + 'user_id' => $this->userId, + 'actor_chat_id' => $this->actorChatId, + ], + static fn(mixed $value): bool => $value !== null, + ); + } + + public function getResultType(): TrueValue + { + return new TrueValue(); + } +} diff --git a/src/Method/DeleteMessageReaction.php b/src/Method/DeleteMessageReaction.php new file mode 100644 index 00000000..8320afa9 --- /dev/null +++ b/src/Method/DeleteMessageReaction.php @@ -0,0 +1,52 @@ + + */ +final readonly class DeleteMessageReaction implements MethodInterface +{ + public function __construct( + private int|string $chatId, + private int $messageId, + private ?int $userId = null, + private ?int $actorChatId = null, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'deleteMessageReaction'; + } + + public function getData(): array + { + return array_filter( + [ + 'chat_id' => $this->chatId, + 'message_id' => $this->messageId, + 'user_id' => $this->userId, + 'actor_chat_id' => $this->actorChatId, + ], + static fn(mixed $value): bool => $value !== null, + ); + } + + public function getResultType(): TrueValue + { + return new TrueValue(); + } +} diff --git a/src/Method/GetChatAdministrators.php b/src/Method/GetChatAdministrators.php index 19f50917..c3c4cfe4 100644 --- a/src/Method/GetChatAdministrators.php +++ b/src/Method/GetChatAdministrators.php @@ -19,6 +19,7 @@ { public function __construct( private int|string $chatId, + private ?bool $returnBots = null, ) {} public function getHttpMethod(): HttpMethod @@ -33,7 +34,13 @@ public function getApiMethod(): string public function getData(): array { - return ['chat_id' => $this->chatId]; + return array_filter( + [ + 'chat_id' => $this->chatId, + 'return_bots' => $this->returnBots, + ], + static fn(mixed $value): bool => $value !== null, + ); } public function getResultType(): ArrayMap diff --git a/src/Method/GetManagedBotAccessSettings.php b/src/Method/GetManagedBotAccessSettings.php new file mode 100644 index 00000000..25b51256 --- /dev/null +++ b/src/Method/GetManagedBotAccessSettings.php @@ -0,0 +1,42 @@ + + */ +final readonly class GetManagedBotAccessSettings implements MethodInterface +{ + public function __construct( + private int $userId, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::GET; + } + + public function getApiMethod(): string + { + return 'getManagedBotAccessSettings'; + } + + public function getData(): array + { + return ['user_id' => $this->userId]; + } + + public function getResultType(): ObjectValue + { + return new ObjectValue(BotAccessSettings::class); + } +} diff --git a/src/Method/GetUserPersonalChatMessages.php b/src/Method/GetUserPersonalChatMessages.php new file mode 100644 index 00000000..52f6d7bd --- /dev/null +++ b/src/Method/GetUserPersonalChatMessages.php @@ -0,0 +1,46 @@ +> + */ +final readonly class GetUserPersonalChatMessages implements MethodInterface +{ + public function __construct( + private int $userId, + private int $limit, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::GET; + } + + public function getApiMethod(): string + { + return 'getUserPersonalChatMessages'; + } + + public function getData(): array + { + return [ + 'user_id' => $this->userId, + 'limit' => $this->limit, + ]; + } + + public function getResultType(): ArrayOfObjectsValue + { + return new ArrayOfObjectsValue(Message::class); + } +} diff --git a/src/Method/Inline/AnswerGuestQuery.php b/src/Method/Inline/AnswerGuestQuery.php new file mode 100644 index 00000000..f76ee7a6 --- /dev/null +++ b/src/Method/Inline/AnswerGuestQuery.php @@ -0,0 +1,47 @@ + + */ +final readonly class AnswerGuestQuery implements MethodInterface +{ + public function __construct( + private string $guestQueryId, + private InlineQueryResult $result, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'answerGuestQuery'; + } + + public function getData(): array + { + return [ + 'guest_query_id' => $this->guestQueryId, + 'result' => $this->result->toRequestArray(), + ]; + } + + public function getResultType(): ObjectValue + { + return new ObjectValue(SentGuestMessage::class); + } +} diff --git a/src/Method/SendLivePhoto.php b/src/Method/SendLivePhoto.php new file mode 100644 index 00000000..34327af5 --- /dev/null +++ b/src/Method/SendLivePhoto.php @@ -0,0 +1,107 @@ + + */ +final readonly class SendLivePhoto implements MethodInterface +{ + /** + * @param MessageEntity[]|null $captionEntities + */ + public function __construct( + private int|string $chatId, + private string|InputFile $livePhoto, + private string|InputFile $photo, + private ?string $businessConnectionId = null, + private ?int $messageThreadId = null, + private ?int $directMessagesTopicId = null, + private ?string $caption = null, + private ?string $parseMode = null, + private ?array $captionEntities = null, + private ?bool $showCaptionAboveMedia = null, + private ?bool $hasSpoiler = null, + private ?bool $disableNotification = null, + private ?bool $protectContent = null, + private ?bool $allowPaidBroadcast = null, + private ?string $messageEffectId = null, + private ?SuggestedPostParameters $suggestedPostParameters = null, + private ?ReplyParameters $replyParameters = null, + private InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'sendLivePhoto'; + } + + public function getData(): array + { + $fileCollector = new FileCollector(); + $livePhoto = $this->livePhoto instanceof InputFile + ? 'attach://' . $fileCollector->add($this->livePhoto) + : $this->livePhoto; + $photo = $this->photo instanceof InputFile + ? 'attach://' . $fileCollector->add($this->photo) + : $this->photo; + + return array_filter( + [ + 'business_connection_id' => $this->businessConnectionId, + 'chat_id' => $this->chatId, + 'message_thread_id' => $this->messageThreadId, + 'direct_messages_topic_id' => $this->directMessagesTopicId, + 'live_photo' => $livePhoto, + 'photo' => $photo, + 'caption' => $this->caption, + 'parse_mode' => $this->parseMode, + 'caption_entities' => $this->captionEntities === null + ? null + : array_map( + static fn(MessageEntity $entity) => $entity->toRequestArray(), + $this->captionEntities, + ), + 'show_caption_above_media' => $this->showCaptionAboveMedia, + 'has_spoiler' => $this->hasSpoiler, + 'disable_notification' => $this->disableNotification, + 'protect_content' => $this->protectContent, + 'allow_paid_broadcast' => $this->allowPaidBroadcast, + 'message_effect_id' => $this->messageEffectId, + 'suggested_post_parameters' => $this->suggestedPostParameters?->toRequestArray(), + 'reply_parameters' => $this->replyParameters?->toRequestArray(), + 'reply_markup' => $this->replyMarkup?->toRequestArray(), + ...$fileCollector->get(), + ], + static fn(mixed $value): bool => $value !== null, + ); + } + + public function getResultType(): ObjectValue + { + return new ObjectValue(Message::class); + } +} diff --git a/src/Method/SendMediaGroup.php b/src/Method/SendMediaGroup.php index e8569ce1..e9b03dda 100644 --- a/src/Method/SendMediaGroup.php +++ b/src/Method/SendMediaGroup.php @@ -10,6 +10,7 @@ use Phptg\BotApi\MethodInterface; use Phptg\BotApi\Type\InputMediaAudio; use Phptg\BotApi\Type\InputMediaDocument; +use Phptg\BotApi\Type\InputMediaLivePhoto; use Phptg\BotApi\Type\InputMediaPhoto; use Phptg\BotApi\Type\InputMediaVideo; use Phptg\BotApi\Type\Message; @@ -23,7 +24,7 @@ final readonly class SendMediaGroup implements MethodInterface { /** - * @param InputMediaAudio[]|InputMediaDocument[]|InputMediaPhoto[]|InputMediaVideo[] $media + * @param InputMediaAudio[]|InputMediaDocument[]|InputMediaLivePhoto[]|InputMediaPhoto[]|InputMediaVideo[] $media */ public function __construct( private int|string $chatId, @@ -53,7 +54,7 @@ public function getData(): array $fileCollector = new FileCollector(); $media = array_map( static function ( - InputMediaAudio|InputMediaDocument|InputMediaPhoto|InputMediaVideo $inputMedia, + InputMediaAudio|InputMediaDocument|InputMediaLivePhoto|InputMediaPhoto|InputMediaVideo $inputMedia, ) use ($fileCollector): array { return $inputMedia->toRequestArray($fileCollector); }, diff --git a/src/Method/SendMessageDraft.php b/src/Method/SendMessageDraft.php index 45401615..872f1f64 100644 --- a/src/Method/SendMessageDraft.php +++ b/src/Method/SendMessageDraft.php @@ -22,7 +22,7 @@ public function __construct( private int $chatId, private int $draftId, - private string $text, + private ?string $text = null, private ?int $messageThreadId = null, private ?string $parseMode = null, private ?array $entities = null, diff --git a/src/Method/SendPoll.php b/src/Method/SendPoll.php index ac9c6933..5feb5355 100644 --- a/src/Method/SendPoll.php +++ b/src/Method/SendPoll.php @@ -5,11 +5,13 @@ namespace Phptg\BotApi\Method; use DateTimeImmutable; +use Phptg\BotApi\FileCollector; use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; +use Phptg\BotApi\Type\InputPollMedia; use Phptg\BotApi\Type\InputPollOption; use Phptg\BotApi\Type\Message; use Phptg\BotApi\Type\MessageEntity; @@ -30,6 +32,7 @@ * @param int[]|null $correctOptionIds * @param MessageEntity[]|null $explanationEntities * @param MessageEntity[]|null $descriptionEntities + * @param string[]|null $countryCodes */ public function __construct( private int|string $chatId, @@ -46,9 +49,11 @@ public function __construct( private ?string $explanation = null, private ?string $explanationParseMode = null, private ?array $explanationEntities = null, + private ?InputPollMedia $explanationMedia = null, private ?int $openPeriod = null, private ?DateTimeImmutable $closeDate = null, private ?bool $isClosed = null, + private ?InputPollMedia $media = null, private ?bool $disableNotification = null, private ?bool $protectContent = null, private ?string $messageEffectId = null, @@ -59,6 +64,8 @@ public function __construct( private ?bool $shuffleOptions = null, private ?bool $allowAddingOptions = null, private ?bool $hideResultsUntilCloses = null, + private ?bool $membersOnly = null, + private ?array $countryCodes = null, private ?string $description = null, private ?string $descriptionParseMode = null, private ?array $descriptionEntities = null, @@ -76,6 +83,14 @@ public function getApiMethod(): string public function getData(): array { + $fileCollector = new FileCollector(); + $options = array_map( + static fn(InputPollOption $option) => $option->toRequestArray($fileCollector), + $this->options, + ); + $explanationMedia = $this->explanationMedia?->toRequestArray($fileCollector); + $media = $this->media?->toRequestArray($fileCollector); + return array_filter( [ 'business_connection_id' => $this->businessConnectionId, @@ -89,10 +104,7 @@ public function getData(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->questionEntities, ), - 'options' => array_map( - static fn(InputPollOption $option) => $option->toRequestArray(), - $this->options, - ), + 'options' => $options, 'is_anonymous' => $this->isAnonymous, 'type' => $this->type, 'allows_multiple_answers' => $this->allowsMultipleAnswers, @@ -100,6 +112,8 @@ public function getData(): array 'shuffle_options' => $this->shuffleOptions, 'allow_adding_options' => $this->allowAddingOptions, 'hide_results_until_closes' => $this->hideResultsUntilCloses, + 'members_only' => $this->membersOnly, + 'country_codes' => $this->countryCodes, 'correct_option_ids' => $this->correctOptionIds, 'explanation' => $this->explanation, 'explanation_parse_mode' => $this->explanationParseMode, @@ -109,6 +123,7 @@ public function getData(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->explanationEntities, ), + 'explanation_media' => $explanationMedia, 'open_period' => $this->openPeriod, 'close_date' => $this->closeDate?->getTimestamp(), 'is_closed' => $this->isClosed, @@ -120,12 +135,14 @@ public function getData(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->descriptionEntities, ), + 'media' => $media, 'disable_notification' => $this->disableNotification, 'protect_content' => $this->protectContent, 'allow_paid_broadcast' => $this->allowPaidBroadcast, 'message_effect_id' => $this->messageEffectId, 'reply_parameters' => $this->replyParameters?->toRequestArray(), 'reply_markup' => $this->replyMarkup?->toRequestArray(), + ...$fileCollector->get(), ], static fn(mixed $value): bool => $value !== null, ); diff --git a/src/Method/SetManagedBotAccessSettings.php b/src/Method/SetManagedBotAccessSettings.php new file mode 100644 index 00000000..e2c4377a --- /dev/null +++ b/src/Method/SetManagedBotAccessSettings.php @@ -0,0 +1,53 @@ + + */ +final readonly class SetManagedBotAccessSettings implements MethodInterface +{ + /** + * @param int[]|null $addedUserIds + */ + public function __construct( + private int $userId, + private bool $isAccessRestricted, + private ?array $addedUserIds = null, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'setManagedBotAccessSettings'; + } + + public function getData(): array + { + return array_filter( + [ + 'user_id' => $this->userId, + 'is_access_restricted' => $this->isAccessRestricted, + 'added_user_ids' => $this->addedUserIds, + ], + static fn(mixed $value): bool => $value !== null, + ); + } + + public function getResultType(): TrueValue + { + return new TrueValue(); + } +} diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 45c9da1d..632be497 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -25,9 +25,11 @@ use Phptg\BotApi\Method\CreateForumTopic; use Phptg\BotApi\Method\DeclineChatJoinRequest; use Phptg\BotApi\Method\DeclineSuggestedPost; +use Phptg\BotApi\Method\DeleteAllMessageReactions; use Phptg\BotApi\Method\DeleteChatPhoto; use Phptg\BotApi\Method\DeleteChatStickerSet; use Phptg\BotApi\Method\DeleteForumTopic; +use Phptg\BotApi\Method\DeleteMessageReaction; use Phptg\BotApi\Method\DeleteMyCommands; use Phptg\BotApi\Method\DeleteStory; use Phptg\BotApi\Method\EditChatInviteLink; @@ -52,6 +54,7 @@ use Phptg\BotApi\Method\GetChatMenuButton; use Phptg\BotApi\Method\GetFile; use Phptg\BotApi\Method\GetForumTopicIconStickers; +use Phptg\BotApi\Method\GetManagedBotAccessSettings; use Phptg\BotApi\Method\GetManagedBotToken; use Phptg\BotApi\Method\GetMe; use Phptg\BotApi\Method\GetMyCommands; @@ -62,10 +65,12 @@ use Phptg\BotApi\Method\GetMyStarBalance; use Phptg\BotApi\Method\GetUserChatBoosts; use Phptg\BotApi\Method\GetUserGifts; +use Phptg\BotApi\Method\GetUserPersonalChatMessages; use Phptg\BotApi\Method\GetUserProfileAudios; use Phptg\BotApi\Method\GetUserProfilePhotos; use Phptg\BotApi\Method\GiftPremiumSubscription; use Phptg\BotApi\Method\HideGeneralForumTopic; +use Phptg\BotApi\Method\Inline\AnswerGuestQuery; use Phptg\BotApi\Method\Inline\AnswerInlineQuery; use Phptg\BotApi\Method\Inline\AnswerWebAppQuery; use Phptg\BotApi\Method\Inline\SavePreparedInlineMessage; @@ -101,6 +106,7 @@ use Phptg\BotApi\Method\SendDice; use Phptg\BotApi\Method\SendDocument; use Phptg\BotApi\Method\SendLocation; +use Phptg\BotApi\Method\SendLivePhoto; use Phptg\BotApi\Method\SendMediaGroup; use Phptg\BotApi\Method\SendMessage; use Phptg\BotApi\Method\SendMessageDraft; @@ -124,6 +130,7 @@ use Phptg\BotApi\Method\SetChatPhoto; use Phptg\BotApi\Method\SetChatStickerSet; use Phptg\BotApi\Method\SetChatTitle; +use Phptg\BotApi\Method\SetManagedBotAccessSettings; use Phptg\BotApi\Method\SetMessageReaction; use Phptg\BotApi\Method\SetMyCommands; use Phptg\BotApi\Method\SetMyDefaultAdministratorRights; @@ -183,6 +190,7 @@ use Phptg\BotApi\Transport\NativeTransport; use Phptg\BotApi\Transport\TransportInterface; use Phptg\BotApi\Type\AcceptedGiftTypes; +use Phptg\BotApi\Type\BotAccessSettings; use Phptg\BotApi\Type\BotCommand; use Phptg\BotApi\Type\BotCommandScope; use Phptg\BotApi\Type\BotDescription; @@ -210,9 +218,11 @@ use Phptg\BotApi\Type\InputMedia; use Phptg\BotApi\Type\InputMediaAudio; use Phptg\BotApi\Type\InputMediaDocument; +use Phptg\BotApi\Type\InputMediaLivePhoto; use Phptg\BotApi\Type\InputMediaPhoto; use Phptg\BotApi\Type\InputMediaVideo; use Phptg\BotApi\Type\InputPaidMedia; +use Phptg\BotApi\Type\InputPollMedia; use Phptg\BotApi\Type\InputPollOption; use Phptg\BotApi\Type\InputProfilePhoto; use Phptg\BotApi\Type\InputStoryContent; @@ -238,6 +248,7 @@ use Phptg\BotApi\Type\Sticker\Sticker; use Phptg\BotApi\Type\Sticker\StickerSet; use Phptg\BotApi\Type\Story; +use Phptg\BotApi\Type\SentGuestMessage; use Phptg\BotApi\Type\SuggestedPostParameters; use Phptg\BotApi\Type\StoryArea; use Phptg\BotApi\Type\Update\Update; @@ -430,6 +441,16 @@ public function answerShippingQuery( ); } + /** + * @see https://core.telegram.org/bots/api#answerguestquery + */ + public function answerGuestQuery(string $guestQueryId, InlineQueryResult $result): FailResult|SentGuestMessage + { + return $this->call( + new AnswerGuestQuery($guestQueryId, $result), + ); + } + /** * @see https://core.telegram.org/bots/api#answerwebappquery */ @@ -737,6 +758,19 @@ public function declineSuggestedPost(int $chatId, int $messageId, ?string $comme ); } + /** + * @see https://core.telegram.org/bots/api#deleteallmessagereactions + */ + public function deleteAllMessageReactions( + int|string $chatId, + ?int $userId = null, + ?int $actorChatId = null, + ): FailResult|true { + return $this->call( + new DeleteAllMessageReactions($chatId, $userId, $actorChatId), + ); + } + /** * @see https://core.telegram.org/bots/api#deletebusinessmessages * @@ -779,6 +813,20 @@ public function deleteForumTopic(int|string $chatId, int $messageThreadId): Fail return $this->call(new DeleteForumTopic($chatId, $messageThreadId)); } + /** + * @see https://core.telegram.org/bots/api#deletemessagereaction + */ + public function deleteMessageReaction( + int|string $chatId, + int $messageId, + ?int $userId = null, + ?int $actorChatId = null, + ): FailResult|true { + return $this->call( + new DeleteMessageReaction($chatId, $messageId, $userId, $actorChatId), + ); + } + /** * @see https://core.telegram.org/bots/api#deletemessage */ @@ -1273,9 +1321,9 @@ public function getChatGifts( * * @return FailResult|ChatMember[] */ - public function getChatAdministrators(int|string $chatId): FailResult|array + public function getChatAdministrators(int|string $chatId, ?bool $returnBots = null): FailResult|array { - return $this->call(new GetChatAdministrators($chatId)); + return $this->call(new GetChatAdministrators($chatId, $returnBots)); } /** @@ -1363,6 +1411,14 @@ public function getManagedBotToken(int $userId): FailResult|string return $this->call(new GetManagedBotToken($userId)); } + /** + * @see https://core.telegram.org/bots/api#getmanagedbotaccesssettings + */ + public function getManagedBotAccessSettings(int $userId): FailResult|BotAccessSettings + { + return $this->call(new GetManagedBotAccessSettings($userId)); + } + /** * @see https://core.telegram.org/bots/api#getmycommands */ @@ -1485,6 +1541,16 @@ public function getUserGifts( ); } + /** + * @see https://core.telegram.org/bots/api#getuserpersonalchatmessages + */ + public function getUserPersonalChatMessages(int $userId, int $limit): FailResult|array + { + return $this->call( + new GetUserPersonalChatMessages($userId, $limit), + ); + } + /** * @see https://core.telegram.org/bots/api#getuserprofileaudios */ @@ -2308,7 +2374,7 @@ public function sendLocation( /** * @see https://core.telegram.org/bots/api#sendmediagroup * - * @param InputMediaAudio[]|InputMediaDocument[]|InputMediaPhoto[]|InputMediaVideo[] $media + * @param InputMediaAudio[]|InputMediaDocument[]|InputMediaLivePhoto[]|InputMediaPhoto[]|InputMediaVideo[] $media * @return FailResult|Message[] */ public function sendMediaGroup( @@ -2339,6 +2405,55 @@ public function sendMediaGroup( ); } + /** + * @param MessageEntity[]|null $captionEntities + * + * @see https://core.telegram.org/bots/api#sendlivephoto + */ + public function sendLivePhoto( + int|string $chatId, + string|InputFile $livePhoto, + string|InputFile $photo, + ?string $businessConnectionId = null, + ?int $messageThreadId = null, + ?int $directMessagesTopicId = null, + ?string $caption = null, + ?string $parseMode = null, + ?array $captionEntities = null, + ?bool $showCaptionAboveMedia = null, + ?bool $hasSpoiler = null, + ?bool $disableNotification = null, + ?bool $protectContent = null, + ?bool $allowPaidBroadcast = null, + ?string $messageEffectId = null, + ?SuggestedPostParameters $suggestedPostParameters = null, + ?ReplyParameters $replyParameters = null, + InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, + ): FailResult|Message { + return $this->call( + new SendLivePhoto( + $chatId, + $livePhoto, + $photo, + $businessConnectionId, + $messageThreadId, + $directMessagesTopicId, + $caption, + $parseMode, + $captionEntities, + $showCaptionAboveMedia, + $hasSpoiler, + $disableNotification, + $protectContent, + $allowPaidBroadcast, + $messageEffectId, + $suggestedPostParameters, + $replyParameters, + $replyMarkup, + ), + ); + } + /** * @param MessageEntity[]|null $entities * @@ -2390,7 +2505,7 @@ public function sendMessage( public function sendMessageDraft( int $chatId, int $draftId, - string $text, + ?string $text = null, ?int $messageThreadId = null, ?string $parseMode = null, ?array $entities = null, @@ -2508,6 +2623,7 @@ public function sendPhoto( * @param int[]|null $correctOptionIds * @param MessageEntity[]|null $explanationEntities * @param MessageEntity[]|null $descriptionEntities + * @param string[]|null $countryCodes * * @see https://core.telegram.org/bots/api#sendpoll */ @@ -2526,9 +2642,11 @@ public function sendPoll( ?string $explanation = null, ?string $explanationParseMode = null, ?array $explanationEntities = null, + ?InputPollMedia $explanationMedia = null, ?int $openPeriod = null, ?DateTimeImmutable $closeDate = null, ?bool $isClosed = null, + ?InputPollMedia $media = null, ?bool $disableNotification = null, ?bool $protectContent = null, ?string $messageEffectId = null, @@ -2539,6 +2657,8 @@ public function sendPoll( ?bool $shuffleOptions = null, ?bool $allowAddingOptions = null, ?bool $hideResultsUntilCloses = null, + ?bool $membersOnly = null, + ?array $countryCodes = null, ?string $description = null, ?string $descriptionParseMode = null, ?array $descriptionEntities = null, @@ -2559,9 +2679,11 @@ public function sendPoll( $explanation, $explanationParseMode, $explanationEntities, + $explanationMedia, $openPeriod, $closeDate, $isClosed, + $media, $disableNotification, $protectContent, $messageEffectId, @@ -2572,6 +2694,8 @@ public function sendPoll( $shuffleOptions, $allowAddingOptions, $hideResultsUntilCloses, + $membersOnly, + $countryCodes, $description, $descriptionParseMode, $descriptionEntities, @@ -2962,6 +3086,18 @@ public function setChatTitle(int|string $chatId, string $title): FailResult|true ); } + /** + * @see https://core.telegram.org/bots/api#setmanagedbotaccesssettings + * + * @param int[]|null $addedUserIds + */ + public function setManagedBotAccessSettings(int $userId, bool $isAccessRestricted, ?array $addedUserIds = null): FailResult|true + { + return $this->call( + new SetManagedBotAccessSettings($userId, $isAccessRestricted, $addedUserIds), + ); + } + /** * @see https://core.telegram.org/bots/api#setcustomemojistickersetthumbnail */ diff --git a/src/Type/BotAccessSettings.php b/src/Type/BotAccessSettings.php new file mode 100644 index 00000000..cea37c31 --- /dev/null +++ b/src/Type/BotAccessSettings.php @@ -0,0 +1,24 @@ + $this->canSendPolls, 'can_send_other_messages' => $this->canSendOtherMessages, 'can_add_web_page_previews' => $this->canAddWebPagePreviews, + 'can_react_to_messages' => $this->canReactToMessages, 'can_edit_tag' => $this->canEditTag, 'can_change_info' => $this->canChangeInfo, 'can_invite_users' => $this->canInviteUsers, diff --git a/src/Type/ExternalReplyInfo.php b/src/Type/ExternalReplyInfo.php index 3681e531..4983db90 100644 --- a/src/Type/ExternalReplyInfo.php +++ b/src/Type/ExternalReplyInfo.php @@ -27,6 +27,7 @@ public function __construct( public ?Animation $animation = null, public ?Audio $audio = null, public ?Document $document = null, + public ?LivePhoto $livePhoto = null, #[ArrayOfObjectsValue(PhotoSize::class)] public ?array $photo = null, public ?Sticker $sticker = null, diff --git a/src/Type/InputMediaAnimation.php b/src/Type/InputMediaAnimation.php index 8a833be8..12da263f 100644 --- a/src/Type/InputMediaAnimation.php +++ b/src/Type/InputMediaAnimation.php @@ -11,7 +11,7 @@ * * @api */ -final readonly class InputMediaAnimation implements InputMedia +final readonly class InputMediaAnimation implements InputMedia, InputPollMedia, InputPollOptionMedia { /** * @param MessageEntity[]|null $captionEntities @@ -34,6 +34,9 @@ public function getType(): string return 'animation'; } + /** + * @return array + */ public function toRequestArray(?FileCollector $fileCollector = null): array { if ($fileCollector !== null) { diff --git a/src/Type/InputMediaAudio.php b/src/Type/InputMediaAudio.php index 17039be3..2d550598 100644 --- a/src/Type/InputMediaAudio.php +++ b/src/Type/InputMediaAudio.php @@ -11,7 +11,7 @@ * * @api */ -final readonly class InputMediaAudio implements InputMedia +final readonly class InputMediaAudio implements InputMedia, InputPollMedia { /** * @param MessageEntity[]|null $captionEntities @@ -32,6 +32,9 @@ public function getType(): string return 'audio'; } + /** + * @return array + */ public function toRequestArray(?FileCollector $fileCollector = null): array { if ($fileCollector !== null) { diff --git a/src/Type/InputMediaDocument.php b/src/Type/InputMediaDocument.php index 9b1b9dca..72c9fd3a 100644 --- a/src/Type/InputMediaDocument.php +++ b/src/Type/InputMediaDocument.php @@ -11,7 +11,7 @@ * * @api */ -final readonly class InputMediaDocument implements InputMedia +final readonly class InputMediaDocument implements InputMedia, InputPollMedia { /** * @param MessageEntity[]|null $captionEntities @@ -30,6 +30,9 @@ public function getType(): string return 'document'; } + /** + * @return array + */ public function toRequestArray(?FileCollector $fileCollector = null): array { if ($fileCollector !== null) { diff --git a/src/Type/InputMediaLivePhoto.php b/src/Type/InputMediaLivePhoto.php new file mode 100644 index 00000000..edc50d35 --- /dev/null +++ b/src/Type/InputMediaLivePhoto.php @@ -0,0 +1,68 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + if ($fileCollector !== null) { + $media = $this->media instanceof InputFile + ? 'attach://' . $fileCollector->add($this->media) + : $this->media; + $photo = $this->photo instanceof InputFile + ? 'attach://' . $fileCollector->add($this->photo) + : $this->photo; + } else { + $media = $this->media; + $photo = $this->photo; + } + + return array_filter( + [ + 'type' => $this->getType(), + 'media' => $media, + 'photo' => $photo, + 'caption' => $this->caption, + 'parse_mode' => $this->parseMode, + 'caption_entities' => $this->captionEntities === null ? null : array_map( + static fn(MessageEntity $entity) => $entity->toRequestArray(), + $this->captionEntities, + ), + 'show_caption_above_media' => $this->showCaptionAboveMedia, + 'has_spoiler' => $this->hasSpoiler, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/src/Type/InputMediaLocation.php b/src/Type/InputMediaLocation.php new file mode 100644 index 00000000..67d11b1b --- /dev/null +++ b/src/Type/InputMediaLocation.php @@ -0,0 +1,42 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + return array_filter( + [ + 'type' => $this->getType(), + 'latitude' => $this->latitude, + 'longitude' => $this->longitude, + 'horizontal_accuracy' => $this->horizontalAccuracy, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/src/Type/InputMediaPhoto.php b/src/Type/InputMediaPhoto.php index 2cf39fea..c81d5851 100644 --- a/src/Type/InputMediaPhoto.php +++ b/src/Type/InputMediaPhoto.php @@ -11,7 +11,7 @@ * * @api */ -final readonly class InputMediaPhoto implements InputMedia +final readonly class InputMediaPhoto implements InputMedia, InputPollMedia, InputPollOptionMedia { /** * @param MessageEntity[]|null $captionEntities @@ -30,6 +30,9 @@ public function getType(): string return 'photo'; } + /** + * @return array + */ public function toRequestArray(?FileCollector $fileCollector = null): array { if ($fileCollector !== null) { diff --git a/src/Type/InputMediaSticker.php b/src/Type/InputMediaSticker.php new file mode 100644 index 00000000..d906effa --- /dev/null +++ b/src/Type/InputMediaSticker.php @@ -0,0 +1,45 @@ +media instanceof InputFile + ? 'attach://' . $fileCollector->add($this->media) + : $this->media; + } else { + $media = $this->media; + } + + return array_filter( + [ + 'type' => $this->getType(), + 'media' => $media, + 'emoji' => $this->emoji, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/src/Type/InputMediaVenue.php b/src/Type/InputMediaVenue.php new file mode 100644 index 00000000..81d96cbf --- /dev/null +++ b/src/Type/InputMediaVenue.php @@ -0,0 +1,52 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + return array_filter( + [ + 'type' => $this->getType(), + 'latitude' => $this->latitude, + 'longitude' => $this->longitude, + 'title' => $this->title, + 'address' => $this->address, + 'foursquare_id' => $this->foursquareId, + 'foursquare_type' => $this->foursquareType, + 'google_place_id' => $this->googlePlaceId, + 'google_place_type' => $this->googlePlaceType, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/src/Type/InputMediaVideo.php b/src/Type/InputMediaVideo.php index ce171c1a..acff6459 100644 --- a/src/Type/InputMediaVideo.php +++ b/src/Type/InputMediaVideo.php @@ -11,7 +11,7 @@ * * @api */ -final readonly class InputMediaVideo implements InputMedia +final readonly class InputMediaVideo implements InputMedia, InputPollMedia, InputPollOptionMedia { /** * @param MessageEntity[]|null $captionEntities @@ -37,6 +37,9 @@ public function getType(): string return 'video'; } + /** + * @return array + */ public function toRequestArray(?FileCollector $fileCollector = null): array { if ($fileCollector !== null) { diff --git a/src/Type/InputPaidMediaLivePhoto.php b/src/Type/InputPaidMediaLivePhoto.php new file mode 100644 index 00000000..4cd69e8d --- /dev/null +++ b/src/Type/InputPaidMediaLivePhoto.php @@ -0,0 +1,46 @@ +media instanceof InputFile + ? 'attach://' . $fileCollector->add($this->media) + : $this->media; + $photo = $this->photo instanceof InputFile + ? 'attach://' . $fileCollector->add($this->photo) + : $this->photo; + } else { + $media = $this->media; + $photo = $this->photo; + } + + return [ + 'type' => $this->getType(), + 'media' => $media, + 'photo' => $photo, + ]; + } +} diff --git a/src/Type/InputPollMedia.php b/src/Type/InputPollMedia.php new file mode 100644 index 00000000..92d6916f --- /dev/null +++ b/src/Type/InputPollMedia.php @@ -0,0 +1,22 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array; +} diff --git a/src/Type/InputPollOption.php b/src/Type/InputPollOption.php index 7c219724..98d7c33a 100644 --- a/src/Type/InputPollOption.php +++ b/src/Type/InputPollOption.php @@ -4,6 +4,8 @@ namespace Phptg\BotApi\Type; +use Phptg\BotApi\FileCollector; + /** * @see https://core.telegram.org/bots/api#inputpolloption * @@ -18,9 +20,10 @@ public function __construct( public ?string $text = null, public ?string $textParseMode = null, public ?array $textEntities = null, + public ?InputPollOptionMedia $media = null, ) {} - public function toRequestArray(): array + public function toRequestArray(?FileCollector $fileCollector = null): array { return array_filter( [ @@ -32,6 +35,7 @@ public function toRequestArray(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->textEntities, ), + 'media' => $this->media?->toRequestArray($fileCollector), ], static fn(mixed $value): bool => $value !== null, ); diff --git a/src/Type/InputPollOptionMedia.php b/src/Type/InputPollOptionMedia.php new file mode 100644 index 00000000..37effced --- /dev/null +++ b/src/Type/InputPollOptionMedia.php @@ -0,0 +1,22 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array; +} diff --git a/src/Type/LivePhoto.php b/src/Type/LivePhoto.php new file mode 100644 index 00000000..b867ff5e --- /dev/null +++ b/src/Type/LivePhoto.php @@ -0,0 +1,30 @@ + $this->addedToAttachmentMenu, 'can_join_groups' => $this->canJoinGroups, 'can_read_all_group_messages' => $this->canReadAllGroupMessages, + 'supports_guest_queries' => $this->supportsGuestQueries, 'supports_inline_queries' => $this->supportsInlineQueries, 'can_connect_to_business' => $this->canConnectToBusiness, 'has_main_web_app' => $this->hasMainWebApp, diff --git a/tests/Method/DeleteAllMessageReactionsTest.php b/tests/Method/DeleteAllMessageReactionsTest.php new file mode 100644 index 00000000..9191f1d6 --- /dev/null +++ b/tests/Method/DeleteAllMessageReactionsTest.php @@ -0,0 +1,55 @@ +getHttpMethod()); + assertSame('deleteAllMessageReactions', $method->getApiMethod()); + assertSame( + [ + 'chat_id' => 1, + ], + $method->getData(), + ); + } + + public function testFull(): void + { + $method = new DeleteAllMessageReactions(1, 123, 456); + + assertSame(HttpMethod::POST, $method->getHttpMethod()); + assertSame('deleteAllMessageReactions', $method->getApiMethod()); + assertSame( + [ + 'chat_id' => 1, + 'user_id' => 123, + 'actor_chat_id' => 456, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new DeleteAllMessageReactions(1); + + $result = TestHelper::createSuccessStubApi(true)->call($method); + + assertTrue($result); + } +} diff --git a/tests/Method/DeleteMessageReactionTest.php b/tests/Method/DeleteMessageReactionTest.php new file mode 100644 index 00000000..eec70bc2 --- /dev/null +++ b/tests/Method/DeleteMessageReactionTest.php @@ -0,0 +1,57 @@ +getHttpMethod()); + assertSame('deleteMessageReaction', $method->getApiMethod()); + assertSame( + [ + 'chat_id' => 1, + 'message_id' => 100, + ], + $method->getData(), + ); + } + + public function testFull(): void + { + $method = new DeleteMessageReaction(1, 100, 123, 456); + + assertSame(HttpMethod::POST, $method->getHttpMethod()); + assertSame('deleteMessageReaction', $method->getApiMethod()); + assertSame( + [ + 'chat_id' => 1, + 'message_id' => 100, + 'user_id' => 123, + 'actor_chat_id' => 456, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new DeleteMessageReaction(1, 100); + + $result = TestHelper::createSuccessStubApi(true)->call($method); + + assertTrue($result); + } +} diff --git a/tests/Method/GetChatAdministratorsTest.php b/tests/Method/GetChatAdministratorsTest.php index c90e74d4..46aa3785 100644 --- a/tests/Method/GetChatAdministratorsTest.php +++ b/tests/Method/GetChatAdministratorsTest.php @@ -30,6 +30,21 @@ public function testBase(): void ); } + public function testWithReturnBots(): void + { + $method = new GetChatAdministrators(1, true); + + assertSame(HttpMethod::GET, $method->getHttpMethod()); + assertSame('getChatAdministrators', $method->getApiMethod()); + assertSame( + [ + 'chat_id' => 1, + 'return_bots' => true, + ], + $method->getData(), + ); + } + public function testPrepareResult(): void { $method = new GetChatAdministrators(1); diff --git a/tests/Method/GetManagedBotAccessSettingsTest.php b/tests/Method/GetManagedBotAccessSettingsTest.php new file mode 100644 index 00000000..50d91f06 --- /dev/null +++ b/tests/Method/GetManagedBotAccessSettingsTest.php @@ -0,0 +1,48 @@ +getHttpMethod()); + assertSame('getManagedBotAccessSettings', $method->getApiMethod()); + assertSame( + [ + 'user_id' => 123, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new GetManagedBotAccessSettings(123); + + $preparedResult = TestHelper::createSuccessStubApi([ + 'is_access_restricted' => true, + 'added_users' => [ + ['id' => 1, 'is_bot' => false, 'first_name' => 'Alice'], + ], + ])->call($method); + + assertInstanceOf(BotAccessSettings::class, $preparedResult); + assertSame(true, $preparedResult->isAccessRestricted); + assertSame(1, $preparedResult->addedUsers[0]->id); + assertSame('Alice', $preparedResult->addedUsers[0]->firstName); + } +} diff --git a/tests/Method/GetUserPersonalChatMessagesTest.php b/tests/Method/GetUserPersonalChatMessagesTest.php new file mode 100644 index 00000000..ec8fe2b5 --- /dev/null +++ b/tests/Method/GetUserPersonalChatMessagesTest.php @@ -0,0 +1,65 @@ +getHttpMethod()); + assertSame('getUserPersonalChatMessages', $method->getApiMethod()); + assertSame( + [ + 'user_id' => 123, + 'limit' => 10, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new GetUserPersonalChatMessages(123, 10); + + $result = TestHelper::createSuccessStubApi([ + [ + 'message_id' => 1, + 'date' => 1620000000, + 'chat' => [ + 'id' => 1, + 'type' => 'private', + ], + ], + [ + 'message_id' => 2, + 'date' => 1620000001, + 'chat' => [ + 'id' => 1, + 'type' => 'private', + ], + ], + ])->call($method); + + assertIsArray($result); + assertCount(2, $result); + assertInstanceOf(Message::class, $result[0]); + assertSame(1, $result[0]->messageId); + assertInstanceOf(Message::class, $result[1]); + assertSame(2, $result[1]->messageId); + } +} diff --git a/tests/Method/Inline/AnswerGuestQueryTest.php b/tests/Method/Inline/AnswerGuestQueryTest.php new file mode 100644 index 00000000..9990f96a --- /dev/null +++ b/tests/Method/Inline/AnswerGuestQueryTest.php @@ -0,0 +1,43 @@ +getHttpMethod()); + assertSame('answerGuestQuery', $method->getApiMethod()); + assertSame( + [ + 'guest_query_id' => 'guest_id_123', + 'result' => $result->toRequestArray(), + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new AnswerGuestQuery('guest_id_456', new InlineQueryResultContact('1', '+79001234567', 'Vjik')); + + $preparedResult = TestHelper::createSuccessStubApi([ + 'inline_message_id' => 'guest_msg_789', + ])->call($method); + + assertSame('guest_msg_789', $preparedResult->inlineMessageId); + } +} diff --git a/tests/Method/SendLivePhotoTest.php b/tests/Method/SendLivePhotoTest.php new file mode 100644 index 00000000..46c31fd3 --- /dev/null +++ b/tests/Method/SendLivePhotoTest.php @@ -0,0 +1,120 @@ +getHttpMethod()); + assertSame('sendLivePhoto', $method->getApiMethod()); + assertSame( + [ + 'chat_id' => 12, + 'live_photo' => 'fid1', + 'photo' => 'fid2', + ], + $method->getData(), + ); + } + + public function testFull(): void + { + $livePhotoFile = new InputFile(null, 'video.mp4'); + $photoFile = new InputFile(null, 'photo.jpg'); + $entity = new MessageEntity('bold', 0, 5); + $replyParameters = new ReplyParameters(23); + $replyMarkup = new ForceReply(); + $method = new SendLivePhoto( + 12, + $livePhotoFile, + $photoFile, + 'bcid1', + 99, + 123, + 'Caption', + 'HTML', + [$entity], + false, + true, + false, + false, + true, + 'meID', + new SuggestedPostParameters( + new SuggestedPostPrice('USD', 10), + ), + $replyParameters, + $replyMarkup, + ); + + assertSame( + [ + 'business_connection_id' => 'bcid1', + 'chat_id' => 12, + 'message_thread_id' => 99, + 'direct_messages_topic_id' => 123, + 'live_photo' => 'attach://file0', + 'photo' => 'attach://file1', + 'caption' => 'Caption', + 'parse_mode' => 'HTML', + 'caption_entities' => [$entity->toRequestArray()], + 'show_caption_above_media' => false, + 'has_spoiler' => true, + 'disable_notification' => false, + 'protect_content' => false, + 'allow_paid_broadcast' => true, + 'message_effect_id' => 'meID', + 'suggested_post_parameters' => (new SuggestedPostParameters( + new SuggestedPostPrice('USD', 10), + ))->toRequestArray(), + 'reply_parameters' => $replyParameters->toRequestArray(), + 'reply_markup' => $replyMarkup->toRequestArray(), + 'file0' => $livePhotoFile, + 'file1' => $photoFile, + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new SendLivePhoto( + 12, + 'fid1', + 'fid2', + ); + + $preparedResult = TestHelper::createSuccessStubApi([ + 'message_id' => 7, + 'date' => 1620000000, + 'chat' => [ + 'id' => 1, + 'type' => 'private', + ], + ])->call($method); + + assertSame(7, $preparedResult->messageId); + } +} diff --git a/tests/Method/SendMessageDraftTest.php b/tests/Method/SendMessageDraftTest.php index f3222ddd..88c345ef 100644 --- a/tests/Method/SendMessageDraftTest.php +++ b/tests/Method/SendMessageDraftTest.php @@ -17,7 +17,7 @@ final class SendMessageDraftTest extends TestCase { public function testBase(): void { - $method = new SendMessageDraft(12, 100, 'hello'); + $method = new SendMessageDraft(12, 100); assertSame(HttpMethod::POST, $method->getHttpMethod()); assertSame('sendMessageDraft', $method->getApiMethod()); @@ -25,7 +25,6 @@ public function testBase(): void [ 'chat_id' => 12, 'draft_id' => 100, - 'text' => 'hello', ], $method->getData(), ); diff --git a/tests/Method/SendPollTest.php b/tests/Method/SendPollTest.php index bf0e555b..d3499f48 100644 --- a/tests/Method/SendPollTest.php +++ b/tests/Method/SendPollTest.php @@ -10,6 +10,8 @@ use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; use Phptg\BotApi\Type\ForceReply; +use Phptg\BotApi\Type\InputFile; +use Phptg\BotApi\Type\InputMediaPhoto; use Phptg\BotApi\Type\InputPollOption; use Phptg\BotApi\Type\MessageEntity; use Phptg\BotApi\Type\ReplyParameters; @@ -41,7 +43,10 @@ public function testBase(): void public function testFull(): void { - $option1 = new InputPollOption('OK'); + $optionFile = new InputFile(null); + $explanationFile = new InputFile(null); + $pollFile = new InputFile(null); + $option1 = new InputPollOption('OK', media: new InputMediaPhoto($optionFile)); $option2 = new InputPollOption('Bad'); $messageEntity1 = new MessageEntity('bold', 0, 5); $messageEntity2 = new MessageEntity('bold', 1, 3); @@ -49,6 +54,8 @@ public function testFull(): void $date = new DateTimeImmutable(); $replyParameters = new ReplyParameters(23); $replyMarkup = new ForceReply(); + $explanationMedia = new InputMediaPhoto($explanationFile); + $pollMedia = new InputMediaPhoto($pollFile); $method = new SendPoll( 12, 'How are you?', @@ -64,9 +71,11 @@ public function testFull(): void 'Good explanation', 'Markdown', [$messageEntity2], + $explanationMedia, 300, $date, true, + $pollMedia, false, false, 'meid2', @@ -77,6 +86,8 @@ public function testFull(): void true, true, true, + false, + ['US', 'GB'], 'Poll description', 'HTML', [$messageEntity3], @@ -93,7 +104,7 @@ public function testFull(): void 'question_parse_mode' => 'HTML', 'question_entities' => [$messageEntity1->toRequestArray()], 'options' => [ - ['text' => 'OK'], + ['text' => 'OK', 'media' => ['type' => 'photo', 'media' => 'attach://file0']], ['text' => 'Bad'], ], 'is_anonymous' => true, @@ -103,22 +114,29 @@ public function testFull(): void 'shuffle_options' => true, 'allow_adding_options' => true, 'hide_results_until_closes' => true, + 'members_only' => false, + 'country_codes' => ['US', 'GB'], 'correct_option_ids' => [0, 1], 'explanation' => 'Good explanation', 'explanation_parse_mode' => 'Markdown', 'explanation_entities' => [$messageEntity2->toRequestArray()], + 'explanation_media' => ['type' => 'photo', 'media' => 'attach://file1'], 'open_period' => 300, 'close_date' => $date->getTimestamp(), 'is_closed' => true, 'description' => 'Poll description', 'description_parse_mode' => 'HTML', 'description_entities' => [$messageEntity3->toRequestArray()], + 'media' => ['type' => 'photo', 'media' => 'attach://file2'], 'disable_notification' => false, 'protect_content' => false, 'allow_paid_broadcast' => true, 'message_effect_id' => 'meid2', 'reply_parameters' => $replyParameters->toRequestArray(), 'reply_markup' => $replyMarkup->toRequestArray(), + 'file0' => $optionFile, + 'file1' => $explanationFile, + 'file2' => $pollFile, ], $method->getData(), ); diff --git a/tests/Method/SetManagedBotAccessSettingsTest.php b/tests/Method/SetManagedBotAccessSettingsTest.php new file mode 100644 index 00000000..1a4f53a5 --- /dev/null +++ b/tests/Method/SetManagedBotAccessSettingsTest.php @@ -0,0 +1,54 @@ +getHttpMethod()); + assertSame('setManagedBotAccessSettings', $method->getApiMethod()); + assertSame( + [ + 'user_id' => 123, + 'is_access_restricted' => true, + ], + $method->getData(), + ); + } + + public function testFull(): void + { + $method = new SetManagedBotAccessSettings(123, true, [456, 789]); + + assertSame( + [ + 'user_id' => 123, + 'is_access_restricted' => true, + 'added_user_ids' => [456, 789], + ], + $method->getData(), + ); + } + + public function testPrepareResult(): void + { + $method = new SetManagedBotAccessSettings(123, true); + + $preparedResult = TestHelper::createSuccessStubApi(true)->call($method); + + assertTrue($preparedResult); + } +} diff --git a/tests/Method/UpdatingMessage/StopPollTest.php b/tests/Method/UpdatingMessage/StopPollTest.php index 9ce26e8e..617a05be 100644 --- a/tests/Method/UpdatingMessage/StopPollTest.php +++ b/tests/Method/UpdatingMessage/StopPollTest.php @@ -62,6 +62,7 @@ public function testPrepareResult(): void 'type' => 'regular', 'allows_multiple_answers' => true, 'allows_revoting' => false, + 'members_only' => true, ])->call($method); assertSame('12', $preparedResult->id); diff --git a/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php b/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php index aa4da919..c6007fbb 100644 --- a/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php +++ b/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php @@ -89,6 +89,7 @@ public static function dataBase(): array 'can_send_polls' => false, 'can_send_other_messages' => false, 'can_add_web_page_previews' => false, + 'can_react_to_messages' => false, 'can_edit_tag' => false, 'can_change_info' => false, 'can_invite_users' => false, diff --git a/tests/TelegramBotApi/TelegramBotApiTest.php b/tests/TelegramBotApi/TelegramBotApiTest.php index 662d513b..00ef28c7 100644 --- a/tests/TelegramBotApi/TelegramBotApiTest.php +++ b/tests/TelegramBotApi/TelegramBotApiTest.php @@ -14,6 +14,7 @@ use Phptg\BotApi\Tests\Support\TransportMock; use Phptg\BotApi\Transport\ApiResponse; use Phptg\BotApi\Type\AcceptedGiftTypes; +use Phptg\BotApi\Type\BotAccessSettings; use Phptg\BotApi\Type\BotCommand; use Phptg\BotApi\Type\BotDescription; use Phptg\BotApi\Type\BotName; @@ -31,6 +32,7 @@ use Phptg\BotApi\Type\Inline\InlineQueryResultGame; use Phptg\BotApi\Type\Inline\PreparedInlineMessage; use Phptg\BotApi\Type\Inline\SentWebAppMessage; +use Phptg\BotApi\Type\SentGuestMessage; use Phptg\BotApi\Type\InputChecklist; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\InputMediaPhoto; @@ -496,6 +498,18 @@ public function testAnswerCallbackQuery(): void assertTrue($result); } + public function testAnswerGuestQuery(): void + { + $api = TestHelper::createSuccessStubApi([ + 'inline_message_id' => 'guest_msg_123', + ]); + + $result = $api->answerGuestQuery('guest_id', new InlineQueryResultContact('1', '+79001234567', 'Vjik')); + + assertInstanceOf(SentGuestMessage::class, $result); + assertSame('guest_msg_123', $result->inlineMessageId); + } + public function testAnswerInlineQuery(): void { $api = TestHelper::createSuccessStubApi(true); @@ -731,6 +745,15 @@ public function testDeclineSuggestedPost(): void assertTrue($result); } + public function testDeleteAllMessageReactions(): void + { + $api = TestHelper::createSuccessStubApi(true); + + $result = $api->deleteAllMessageReactions(1); + + assertTrue($result); + } + public function testDeleteBusinessMessages(): void { $api = TestHelper::createSuccessStubApi(true); @@ -767,6 +790,15 @@ public function testDeleteForumTopic(): void assertTrue($result); } + public function testDeleteMessageReaction(): void + { + $api = TestHelper::createSuccessStubApi(true); + + $result = $api->deleteMessageReaction(1, 100); + + assertTrue($result); + } + public function testDeleteMessage(): void { $api = TestHelper::createSuccessStubApi(true); @@ -1268,6 +1300,23 @@ public function testGetManagedBotToken(): void assertSame('123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11', $result); } + public function testGetManagedBotAccessSettings(): void + { + $api = TestHelper::createSuccessStubApi([ + 'is_access_restricted' => true, + 'added_users' => [ + ['id' => 1, 'is_bot' => false, 'first_name' => 'Alice'], + ], + ]); + + $result = $api->getManagedBotAccessSettings(789); + + assertInstanceOf(BotAccessSettings::class, $result); + assertSame(true, $result->isAccessRestricted); + assertSame(1, $result->addedUsers[0]->id); + assertSame('Alice', $result->addedUsers[0]->firstName); + } + public function testGetMyCommands(): void { $api = TestHelper::createSuccessStubApi([ @@ -1527,6 +1576,26 @@ public function testGetUserGifts(): void assertInstanceOf(OwnedGifts::class, $result); } + public function testGetUserPersonalChatMessages(): void + { + $api = TestHelper::createSuccessStubApi([ + [ + 'message_id' => 1, + 'date' => 1620000000, + 'chat' => [ + 'id' => 1, + 'type' => 'private', + ], + ], + ]); + + $result = $api->getUserPersonalChatMessages(123, 10); + + assertIsArray($result); + assertCount(1, $result); + assertInstanceOf(Message::class, $result[0]); + } + public function testGetUserProfileAudios(): void { $api = TestHelper::createSuccessStubApi([ @@ -2052,6 +2121,23 @@ public function testSendLocation(): void assertSame(7, $result->messageId); } + public function testSendLivePhoto(): void + { + $api = TestHelper::createSuccessStubApi([ + 'message_id' => 7, + 'date' => 1620000000, + 'chat' => [ + 'id' => 1, + 'type' => 'private', + ], + ]); + + $result = $api->sendLivePhoto(12, 'fid1', 'fid2'); + + assertInstanceOf(Message::class, $result); + assertSame(7, $result->messageId); + } + public function testSendMediaGroup(): void { $api = TestHelper::createSuccessStubApi([ @@ -2094,7 +2180,7 @@ public function testSendMessageDraft(): void { $api = TestHelper::createSuccessStubApi(true); - $result = $api->sendMessageDraft(12, 100, 'hello'); + $result = $api->sendMessageDraft(12, 100); assertTrue($result); } @@ -2308,6 +2394,15 @@ public function testSetChatTitle(): void assertTrue($result); } + public function testSetManagedBotAccessSettings(): void + { + $api = TestHelper::createSuccessStubApi(true); + + $result = $api->setManagedBotAccessSettings(789, true, [1, 2]); + + assertTrue($result); + } + public function testSetCustomEmojiStickerSetThumbnail(): void { $api = TestHelper::createSuccessStubApi(true); @@ -2499,6 +2594,7 @@ public function testStopPoll(): void 'type' => 'regular', 'allows_multiple_answers' => true, 'allows_revoting' => false, + 'members_only' => true, ]); $result = $api->stopPoll(1, 2); diff --git a/tests/Type/BotAccessSettingsTest.php b/tests/Type/BotAccessSettingsTest.php new file mode 100644 index 00000000..0fba5de2 --- /dev/null +++ b/tests/Type/BotAccessSettingsTest.php @@ -0,0 +1,55 @@ +isAccessRestricted); + assertNull($settings->addedUsers); + } + + public function testFull(): void + { + $user1 = new User(1, false, 'Alice'); + $user2 = new User(2, false, 'Bob'); + $settings = new BotAccessSettings(true, [$user1, $user2]); + + assertSame(true, $settings->isAccessRestricted); + assertSame([$user1, $user2], $settings->addedUsers); + } + + public function testFromTelegramResult(): void + { + $settings = (new ObjectFactory())->create([ + 'is_access_restricted' => true, + 'added_users' => [ + ['id' => 1, 'is_bot' => false, 'first_name' => 'Alice'], + ['id' => 2, 'is_bot' => false, 'first_name' => 'Bob'], + ], + ], null, BotAccessSettings::class); + + assertInstanceOf(BotAccessSettings::class, $settings); + assertSame(true, $settings->isAccessRestricted); + assertInstanceOf(User::class, $settings->addedUsers[0]); + assertSame(1, $settings->addedUsers[0]->id); + assertSame('Alice', $settings->addedUsers[0]->firstName); + assertInstanceOf(User::class, $settings->addedUsers[1]); + assertSame(2, $settings->addedUsers[1]->id); + assertSame('Bob', $settings->addedUsers[1]->firstName); + } +} diff --git a/tests/Type/ChatMemberRestrictedTest.php b/tests/Type/ChatMemberRestrictedTest.php index bed96e7a..2119ac6a 100644 --- a/tests/Type/ChatMemberRestrictedTest.php +++ b/tests/Type/ChatMemberRestrictedTest.php @@ -39,6 +39,7 @@ public function testBase(): void true, true, true, + true, false, ); @@ -56,6 +57,7 @@ public function testBase(): void assertTrue($member->canSendPolls); assertTrue($member->canSendOtherMessages); assertTrue($member->canAddWebPagePreviews); + assertTrue($member->canReactToMessages); assertTrue($member->canEditTag); assertTrue($member->canChangeInfo); assertTrue($member->canInviteUsers); @@ -84,6 +86,7 @@ public function testFromTelegramResult(): void 'can_send_polls' => true, 'can_send_other_messages' => true, 'can_add_web_page_previews' => true, + 'can_react_to_messages' => true, 'can_edit_tag' => true, 'can_change_info' => true, 'can_invite_users' => true, @@ -105,6 +108,7 @@ public function testFromTelegramResult(): void assertTrue($member->canSendPolls); assertTrue($member->canSendOtherMessages); assertTrue($member->canAddWebPagePreviews); + assertTrue($member->canReactToMessages); assertTrue($member->canEditTag); assertTrue($member->canChangeInfo); assertTrue($member->canInviteUsers); @@ -133,6 +137,7 @@ public function testFromTelegramResultWithZeroUntilDate(): void 'can_send_polls' => true, 'can_send_other_messages' => true, 'can_add_web_page_previews' => true, + 'can_react_to_messages' => true, 'can_edit_tag' => true, 'can_change_info' => true, 'can_invite_users' => true, @@ -153,6 +158,7 @@ public function testFromTelegramResultWithZeroUntilDate(): void assertTrue($member->canSendPolls); assertTrue($member->canSendOtherMessages); assertTrue($member->canAddWebPagePreviews); + assertTrue($member->canReactToMessages); assertTrue($member->canEditTag); assertTrue($member->canChangeInfo); assertTrue($member->canInviteUsers); diff --git a/tests/Type/ChatPermissionsTest.php b/tests/Type/ChatPermissionsTest.php index 844467d4..1fe7fb08 100644 --- a/tests/Type/ChatPermissionsTest.php +++ b/tests/Type/ChatPermissionsTest.php @@ -29,6 +29,7 @@ public function testBase(): void assertNull($chatPermissions->canSendPolls); assertNull($chatPermissions->canSendOtherMessages); assertNull($chatPermissions->canAddWebPagePreviews); + assertNull($chatPermissions->canReactToMessages); assertNull($chatPermissions->canEditTag); assertNull($chatPermissions->canChangeInfo); assertNull($chatPermissions->canInviteUsers); @@ -51,6 +52,7 @@ public function testFull(): void true, true, true, + true, false, false, true, @@ -69,6 +71,7 @@ public function testFull(): void 'can_send_polls' => false, 'can_send_other_messages' => true, 'can_add_web_page_previews' => true, + 'can_react_to_messages' => true, 'can_edit_tag' => true, 'can_change_info' => false, 'can_invite_users' => false, @@ -92,6 +95,7 @@ public function testFromTelegramResult(): void 'can_send_polls' => true, 'can_send_other_messages' => true, 'can_add_web_page_previews' => true, + 'can_react_to_messages' => true, 'can_edit_tag' => true, 'can_change_info' => true, 'can_invite_users' => true, @@ -109,6 +113,7 @@ public function testFromTelegramResult(): void assertTrue($chatPermissions->canSendPolls); assertTrue($chatPermissions->canSendOtherMessages); assertTrue($chatPermissions->canAddWebPagePreviews); + assertTrue($chatPermissions->canReactToMessages); assertTrue($chatPermissions->canEditTag); assertTrue($chatPermissions->canChangeInfo); assertTrue($chatPermissions->canInviteUsers); diff --git a/tests/Type/ExternalReplyInfoTest.php b/tests/Type/ExternalReplyInfoTest.php index b6a79694..bffd8cb1 100644 --- a/tests/Type/ExternalReplyInfoTest.php +++ b/tests/Type/ExternalReplyInfoTest.php @@ -35,6 +35,7 @@ public function testBase(): void assertNull($externalReplyInfo->animation); assertNull($externalReplyInfo->audio); assertNull($externalReplyInfo->document); + assertNull($externalReplyInfo->livePhoto); assertNull($externalReplyInfo->photo); assertNull($externalReplyInfo->sticker); assertNull($externalReplyInfo->story); @@ -91,6 +92,13 @@ public function testFromTelegramResult(): void 'file_id' => 'f3', 'file_unique_id' => 'fu3', ], + 'live_photo' => [ + 'file_id' => 'f3lp', + 'file_unique_id' => 'fu3lp', + 'width' => 640, + 'height' => 480, + 'duration' => 3, + ], 'photo' => [ [ 'file_id' => 'f4', @@ -183,6 +191,7 @@ public function testFromTelegramResult(): void 'type' => 'regular', 'allows_multiple_answers' => true, 'allows_revoting' => false, + 'members_only' => true, ], 'venue' => [ 'location' => [ @@ -219,6 +228,10 @@ public function testFromTelegramResult(): void assertSame('f1', $externalReplyInfo->animation?->fileId); assertSame('f2', $externalReplyInfo->audio?->fileId); assertSame('f3', $externalReplyInfo->document?->fileId); + assertSame('f3lp', $externalReplyInfo->livePhoto?->fileId); + assertSame(640, $externalReplyInfo->livePhoto?->width); + assertSame(480, $externalReplyInfo->livePhoto?->height); + assertSame(3, $externalReplyInfo->livePhoto?->duration); assertCount(1, $externalReplyInfo->photo); assertSame('f4', $externalReplyInfo->photo[0]->fileId); diff --git a/tests/Type/InputMediaLivePhotoTest.php b/tests/Type/InputMediaLivePhotoTest.php new file mode 100644 index 00000000..247bbd42 --- /dev/null +++ b/tests/Type/InputMediaLivePhotoTest.php @@ -0,0 +1,84 @@ +getType()); + assertSame( + [ + 'type' => 'live_photo', + 'media' => 'https://example.com/video.mp4', + 'photo' => 'https://example.com/photo.jpg', + ], + $inputMedia->toRequestArray(), + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'live_photo', + 'media' => 'https://example.com/video.mp4', + 'photo' => 'https://example.com/photo.jpg', + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertEmpty($fileCollector->get()); + } + + public function testFull(): void + { + $mediaFile = new InputFile(null); + $photoFile = new InputFile(null); + $entity = new MessageEntity('bold', 0, 4); + $inputMedia = new InputMediaLivePhoto( + $mediaFile, + $photoFile, + 'Hello', + 'HTML', + [$entity], + false, + true, + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'live_photo', + 'media' => 'attach://file0', + 'photo' => 'attach://file1', + 'caption' => 'Hello', + 'parse_mode' => 'HTML', + 'caption_entities' => [$entity->toRequestArray()], + 'show_caption_above_media' => false, + 'has_spoiler' => true, + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertSame( + [ + 'file0' => $mediaFile, + 'file1' => $photoFile, + ], + $fileCollector->get(), + ); + } +} diff --git a/tests/Type/InputMediaLocationTest.php b/tests/Type/InputMediaLocationTest.php new file mode 100644 index 00000000..94ddb91f --- /dev/null +++ b/tests/Type/InputMediaLocationTest.php @@ -0,0 +1,57 @@ +getType()); + assertSame( + [ + 'type' => 'location', + 'latitude' => 55.7558, + 'longitude' => 37.6173, + ], + $inputMedia->toRequestArray(), + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'location', + 'latitude' => 55.7558, + 'longitude' => 37.6173, + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertEmpty($fileCollector->get()); + } + + public function testFull(): void + { + $inputMedia = new InputMediaLocation(55.7558, 37.6173, 500.5); + + assertSame('location', $inputMedia->getType()); + assertSame( + [ + 'type' => 'location', + 'latitude' => 55.7558, + 'longitude' => 37.6173, + 'horizontal_accuracy' => 500.5, + ], + $inputMedia->toRequestArray(), + ); + } +} diff --git a/tests/Type/InputMediaStickerTest.php b/tests/Type/InputMediaStickerTest.php new file mode 100644 index 00000000..a12354ca --- /dev/null +++ b/tests/Type/InputMediaStickerTest.php @@ -0,0 +1,58 @@ +getType()); + assertSame( + [ + 'type' => 'sticker', + 'media' => 'https://example.com/sticker.webp', + ], + $inputMedia->toRequestArray(), + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'sticker', + 'media' => 'https://example.com/sticker.webp', + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertEmpty($fileCollector->get()); + } + + public function testFull(): void + { + $media = new InputFile(null); + $inputMedia = new InputMediaSticker($media, '😀'); + $fileCollector = new FileCollector(); + + assertSame('sticker', $inputMedia->getType()); + assertSame( + [ + 'type' => 'sticker', + 'media' => 'attach://file0', + 'emoji' => '😀', + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertSame(['file0' => $media], $fileCollector->get()); + } +} diff --git a/tests/Type/InputMediaVenueTest.php b/tests/Type/InputMediaVenueTest.php new file mode 100644 index 00000000..9b5ed4e6 --- /dev/null +++ b/tests/Type/InputMediaVenueTest.php @@ -0,0 +1,75 @@ +getType()); + assertSame( + [ + 'type' => 'venue', + 'latitude' => 55.7558, + 'longitude' => 37.6173, + 'title' => 'The Kremlin', + 'address' => 'Red Square, Moscow', + ], + $inputMedia->toRequestArray(), + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'venue', + 'latitude' => 55.7558, + 'longitude' => 37.6173, + 'title' => 'The Kremlin', + 'address' => 'Red Square, Moscow', + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertEmpty($fileCollector->get()); + } + + public function testFull(): void + { + $inputMedia = new InputMediaVenue( + 55.7558, + 37.6173, + 'The Kremlin', + 'Red Square, Moscow', + 'foursquare_id_123', + 'arts_entertainment/landmark', + 'google_place_id_456', + 'point_of_interest', + ); + + assertSame('venue', $inputMedia->getType()); + assertSame( + [ + 'type' => 'venue', + 'latitude' => 55.7558, + 'longitude' => 37.6173, + 'title' => 'The Kremlin', + 'address' => 'Red Square, Moscow', + 'foursquare_id' => 'foursquare_id_123', + 'foursquare_type' => 'arts_entertainment/landmark', + 'google_place_id' => 'google_place_id_456', + 'google_place_type' => 'point_of_interest', + ], + $inputMedia->toRequestArray(), + ); + } +} diff --git a/tests/Type/InputPaidMediaLivePhotoTest.php b/tests/Type/InputPaidMediaLivePhotoTest.php new file mode 100644 index 00000000..0e6163f9 --- /dev/null +++ b/tests/Type/InputPaidMediaLivePhotoTest.php @@ -0,0 +1,75 @@ +getType()); + assertSame( + [ + 'type' => 'live_photo', + 'media' => 'file_id_video', + 'photo' => 'file_id_photo', + ], + $inputMedia->toRequestArray(), + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'live_photo', + 'media' => 'file_id_video', + 'photo' => 'file_id_photo', + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertSame([], $fileCollector->get()); + } + + public function testFull(): void + { + $mediaFile = new InputFile(null); + $photoFile = new InputFile(null); + $inputMedia = new InputPaidMediaLivePhoto($mediaFile, $photoFile); + + assertSame('live_photo', $inputMedia->getType()); + assertSame( + [ + 'type' => 'live_photo', + 'media' => $mediaFile, + 'photo' => $photoFile, + ], + $inputMedia->toRequestArray(), + ); + + $fileCollector = new FileCollector(); + assertSame( + [ + 'type' => 'live_photo', + 'media' => 'attach://file0', + 'photo' => 'attach://file1', + ], + $inputMedia->toRequestArray($fileCollector), + ); + assertSame( + [ + 'file0' => $mediaFile, + 'file1' => $photoFile, + ], + $fileCollector->get(), + ); + } +} diff --git a/tests/Type/InputPollOptionTest.php b/tests/Type/InputPollOptionTest.php index 3f208343..6951bc64 100644 --- a/tests/Type/InputPollOptionTest.php +++ b/tests/Type/InputPollOptionTest.php @@ -5,6 +5,7 @@ namespace Phptg\BotApi\Tests\Type; use PHPUnit\Framework\TestCase; +use Phptg\BotApi\Type\InputMediaPhoto; use Phptg\BotApi\Type\InputPollOption; use Phptg\BotApi\Type\MessageEntity; @@ -20,6 +21,7 @@ public function testBase(): void assertNull($option->text); assertNull($option->textParseMode); assertNull($option->textEntities); + assertNull($option->media); assertSame([], $option->toRequestArray()); } @@ -27,17 +29,20 @@ public function testBase(): void public function testFilled(): void { $messageEntity = new MessageEntity('bold', 0, 4); - $option = new InputPollOption('test', 'MarkdownV2', [$messageEntity]); + $media = new InputMediaPhoto('file_id1'); + $option = new InputPollOption('test', 'MarkdownV2', [$messageEntity], $media); assertSame('test', $option->text); assertSame('MarkdownV2', $option->textParseMode); assertSame([$messageEntity], $option->textEntities); + assertSame($media, $option->media); assertSame( [ 'text' => 'test', 'text_parse_mode' => 'MarkdownV2', 'text_entities' => [$messageEntity->toRequestArray()], + 'media' => $media->toRequestArray(), ], $option->toRequestArray(), ); diff --git a/tests/Type/LivePhotoTest.php b/tests/Type/LivePhotoTest.php new file mode 100644 index 00000000..634183ab --- /dev/null +++ b/tests/Type/LivePhotoTest.php @@ -0,0 +1,74 @@ +fileId); + assertSame('file_unique_id_456', $livePhoto->fileUniqueId); + assertSame(1280, $livePhoto->width); + assertSame(720, $livePhoto->height); + assertSame(5, $livePhoto->duration); + assertNull($livePhoto->photo); + assertNull($livePhoto->mimeType); + assertNull($livePhoto->fileSize); + } + + public function testFromTelegramResult(): void + { + $livePhoto = (new ObjectFactory())->create([ + 'file_id' => 'file_id_abc', + 'file_unique_id' => 'file_unique_id_def', + 'width' => 1920, + 'height' => 1080, + 'duration' => 10, + 'photo' => [ + [ + 'file_id' => 'photo_file_id_1', + 'file_unique_id' => 'photo_unique_id_1', + 'width' => 1920, + 'height' => 1080, + ], + [ + 'file_id' => 'photo_file_id_2', + 'file_unique_id' => 'photo_unique_id_2', + 'width' => 960, + 'height' => 540, + ], + ], + 'mime_type' => 'video/mp4', + 'file_size' => 2097152, + ], null, LivePhoto::class); + + assertSame('file_id_abc', $livePhoto->fileId); + assertSame('file_unique_id_def', $livePhoto->fileUniqueId); + assertSame(1920, $livePhoto->width); + assertSame(1080, $livePhoto->height); + assertSame(10, $livePhoto->duration); + assertCount(2, $livePhoto->photo); + assertSame('photo_file_id_1', $livePhoto->photo[0]->fileId); + assertSame('photo_file_id_2', $livePhoto->photo[1]->fileId); + assertSame('video/mp4', $livePhoto->mimeType); + assertSame(2097152, $livePhoto->fileSize); + } +} diff --git a/tests/Type/MessageTest.php b/tests/Type/MessageTest.php index 7d7e3699..aafd4dee 100644 --- a/tests/Type/MessageTest.php +++ b/tests/Type/MessageTest.php @@ -51,6 +51,7 @@ public function testBase(): void assertNull($message->senderBusinessBot); assertNull($message->senderTag); assertNull($message->businessConnectionId); + assertNull($message->guestQueryId); assertNull($message->forwardOrigin); assertNull($message->isTopicMessage); assertNull($message->isAutomaticForward); @@ -59,6 +60,8 @@ public function testBase(): void assertNull($message->quote); assertNull($message->replyToStory); assertNull($message->viaBot); + assertNull($message->guestBotCallerUser); + assertNull($message->guestBotCallerChat); assertNull($message->editDate); assertNull($message->hasProtectedContent); assertNull($message->isFromOffline); @@ -73,6 +76,7 @@ public function testBase(): void assertNull($message->animation); assertNull($message->audio); assertNull($message->document); + assertNull($message->livePhoto); assertNull($message->photo); assertNull($message->sticker); assertNull($message->story); @@ -182,6 +186,7 @@ public function testFromTelegramResult(): void ], 'sender_tag' => 'admin-tag', 'business_connection_id' => 'btest', + 'guest_query_id' => 'guest_query_123', 'forward_origin' => [ 'type' => 'hidden_user', 'date' => 1234156479, @@ -222,6 +227,15 @@ public function testFromTelegramResult(): void 'is_bot' => false, 'first_name' => 'John6Bot', ], + 'guest_bot_caller_user' => [ + 'id' => 128, + 'is_bot' => false, + 'first_name' => 'GuestCaller', + ], + 'guest_bot_caller_chat' => [ + 'id' => 129, + 'type' => 'private', + ], 'edit_date' => 65416841123, 'has_protected_content' => true, 'is_from_offline' => true, @@ -265,6 +279,13 @@ public function testFromTelegramResult(): void 'file_id' => 'f3', 'file_unique_id' => 'fu3', ], + 'live_photo' => [ + 'file_id' => 'f3lp', + 'file_unique_id' => 'fu3lp', + 'width' => 640, + 'height' => 480, + 'duration' => 3, + ], 'photo' => [ [ 'file_id' => 'f4', @@ -340,6 +361,7 @@ public function testFromTelegramResult(): void 'type' => 'regular', 'allows_multiple_answers' => true, 'allows_revoting' => false, + 'members_only' => true, ], 'venue' => [ 'location' => [ @@ -684,6 +706,7 @@ public function testFromTelegramResult(): void assertSame(15, $message->senderBusinessBot?->id); assertSame('admin-tag', $message->senderTag); assertSame('btest', $message->businessConnectionId); + assertSame('guest_query_123', $message->guestQueryId); assertInstanceOf(MessageOriginHiddenUser::class, $message->forwardOrigin); assertSame('bat', $message->forwardOrigin?->senderUserName); @@ -698,6 +721,8 @@ public function testFromTelegramResult(): void assertSame('test93', $message->quote?->text); assertSame(8863, $message->replyToStory?->id); assertSame(127, $message->viaBot?->id); + assertSame(128, $message->guestBotCallerUser?->id); + assertSame(129, $message->guestBotCallerChat?->id); assertSame(65416841123, $message->editDate?->getTimestamp()); assertTrue($message->hasProtectedContent); assertTrue($message->isFromOffline); @@ -715,6 +740,10 @@ public function testFromTelegramResult(): void assertSame('an1', $message->animation?->fileId); assertSame('f2', $message->audio?->fileId); assertSame('f3', $message->document?->fileId); + assertSame('f3lp', $message->livePhoto?->fileId); + assertSame(640, $message->livePhoto?->width); + assertSame(480, $message->livePhoto?->height); + assertSame(3, $message->livePhoto?->duration); assertCount(1, $message->photo); assertSame('f4', $message->photo[0]->fileId); diff --git a/tests/Type/PaidMediaLivePhotoTest.php b/tests/Type/PaidMediaLivePhotoTest.php new file mode 100644 index 00000000..6a7f6ace --- /dev/null +++ b/tests/Type/PaidMediaLivePhotoTest.php @@ -0,0 +1,47 @@ +getType()); + assertSame($livePhoto, $type->livePhoto); + } + + public function testFromTelegramResult(): void + { + $type = (new ObjectFactory())->create([ + 'type' => 'live_photo', + 'live_photo' => [ + 'file_id' => 'fileId', + 'file_unique_id' => 'fileUniqueId', + 'width' => 640, + 'height' => 480, + 'duration' => 3, + ], + ], null, PaidMediaLivePhoto::class); + + assertSame('live_photo', $type->getType()); + assertInstanceOf(LivePhoto::class, $type->livePhoto); + assertSame('fileId', $type->livePhoto->fileId); + assertSame('fileUniqueId', $type->livePhoto->fileUniqueId); + assertSame(640, $type->livePhoto->width); + assertSame(480, $type->livePhoto->height); + assertSame(3, $type->livePhoto->duration); + } +} diff --git a/tests/Type/PollMediaTest.php b/tests/Type/PollMediaTest.php new file mode 100644 index 00000000..b88cc376 --- /dev/null +++ b/tests/Type/PollMediaTest.php @@ -0,0 +1,114 @@ +animation); + assertNull($pollMedia->audio); + assertNull($pollMedia->document); + assertNull($pollMedia->livePhoto); + assertNull($pollMedia->location); + assertNull($pollMedia->photo); + assertNull($pollMedia->sticker); + assertNull($pollMedia->venue); + assertNull($pollMedia->video); + } + + public function testFromTelegramResult(): void + { + $pollMedia = (new ObjectFactory())->create([ + 'animation' => [ + 'file_id' => 'animation_file_id', + 'file_unique_id' => 'animation_unique_id', + 'width' => 1280, + 'height' => 720, + 'duration' => 10, + ], + 'audio' => [ + 'file_id' => 'audio_file_id', + 'file_unique_id' => 'audio_unique_id', + 'duration' => 180, + ], + 'document' => [ + 'file_id' => 'document_file_id', + 'file_unique_id' => 'document_unique_id', + ], + 'live_photo' => [ + 'file_id' => 'live_photo_file_id', + 'file_unique_id' => 'live_photo_unique_id', + 'width' => 1080, + 'height' => 1920, + 'duration' => 5, + ], + 'location' => [ + 'latitude' => 55.7558, + 'longitude' => 37.6173, + ], + 'photo' => [ + [ + 'file_id' => 'photo_file_id_1', + 'file_unique_id' => 'photo_unique_id_1', + 'width' => 1920, + 'height' => 1080, + ], + [ + 'file_id' => 'photo_file_id_2', + 'file_unique_id' => 'photo_unique_id_2', + 'width' => 960, + 'height' => 540, + ], + ], + 'sticker' => [ + 'file_id' => 'sticker_file_id', + 'file_unique_id' => 'sticker_unique_id', + 'type' => 'regular', + 'width' => 512, + 'height' => 512, + 'is_animated' => false, + 'is_video' => false, + ], + 'venue' => [ + 'location' => [ + 'latitude' => 55.7558, + 'longitude' => 37.6173, + ], + 'title' => 'Kremlin', + 'address' => 'Red Square, Moscow', + ], + 'video' => [ + 'file_id' => 'video_file_id', + 'file_unique_id' => 'video_unique_id', + 'width' => 1920, + 'height' => 1080, + 'duration' => 30, + ], + ], null, PollMedia::class); + + assertSame('animation_file_id', $pollMedia->animation?->fileId); + assertSame('audio_file_id', $pollMedia->audio?->fileId); + assertSame('document_file_id', $pollMedia->document?->fileId); + assertSame('live_photo_file_id', $pollMedia->livePhoto?->fileId); + assertSame(55.7558, $pollMedia->location?->latitude); + assertCount(2, $pollMedia->photo); + assertSame('photo_file_id_1', $pollMedia->photo[0]->fileId); + assertSame('photo_file_id_2', $pollMedia->photo[1]->fileId); + assertSame('sticker_file_id', $pollMedia->sticker?->fileId); + assertSame('Kremlin', $pollMedia->venue?->title); + assertSame('video_file_id', $pollMedia->video?->fileId); + } +} diff --git a/tests/Type/PollOptionTest.php b/tests/Type/PollOptionTest.php index 0e31244c..c21f07c7 100644 --- a/tests/Type/PollOptionTest.php +++ b/tests/Type/PollOptionTest.php @@ -22,6 +22,7 @@ public function testBase(): void assertSame('A', $option->text); assertSame(25, $option->voterCount); assertNull($option->textEntities); + assertNull($option->media); assertNull($option->addedByUser); assertNull($option->addedByChat); assertNull($option->additionDate); @@ -40,6 +41,15 @@ public function testFromTelegramResult(): void 'type' => 'bold', ], ], + 'media' => [ + 'video' => [ + 'file_id' => 'video_file_id', + 'file_unique_id' => 'video_unique_id', + 'width' => 1920, + 'height' => 1080, + 'duration' => 30, + ], + ], 'added_by_user' => [ 'id' => 42, 'is_bot' => false, @@ -59,6 +69,8 @@ public function testFromTelegramResult(): void assertCount(1, $option->textEntities); assertSame(23, $option->textEntities[0]->offset); + assertSame('video_file_id', $option->media?->video?->fileId); + 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 df3f5b40..7e39a0b0 100644 --- a/tests/Type/PollTest.php +++ b/tests/Type/PollTest.php @@ -30,6 +30,7 @@ public function testBase(): void 'regular', true, false, + true, ); assertSame('12', $poll->id); @@ -41,13 +42,18 @@ public function testBase(): void assertSame('regular', $poll->type); assertTrue($poll->allowsMultipleAnswers); assertFalse($poll->allowsRevoting); + assertTrue($poll->membersOnly); + assertNull($poll->countryCodes); + assertNull($poll->questionEntities); assertNull($poll->correctOptionIds); assertNull($poll->explanation); assertNull($poll->explanationEntities); + assertNull($poll->explanationMedia); assertNull($poll->openPeriod); assertNull($poll->closeDate); assertNull($poll->description); assertNull($poll->descriptionEntities); + assertNull($poll->media); } public function testFromTelegramResult(): void @@ -64,6 +70,7 @@ public function testFromTelegramResult(): void 'type' => 'regular', 'allows_multiple_answers' => true, 'allows_revoting' => true, + 'members_only' => true, 'question_entities' => [ [ 'offset' => 0, @@ -90,6 +97,24 @@ public function testFromTelegramResult(): void 'type' => 'bold', ], ], + 'explanation_media' => [ + 'location' => [ + 'latitude' => 55.7558, + 'longitude' => 37.6173, + ], + ], + 'media' => [ + 'sticker' => [ + 'file_id' => 'sticker_file_id', + 'file_unique_id' => 'sticker_unique_id', + 'type' => 'regular', + 'width' => 512, + 'height' => 512, + 'is_animated' => false, + 'is_video' => false, + ], + ], + 'country_codes' => ['US', 'GB', 'DE'], ], null, Poll::class); assertSame('12', $poll->id); @@ -105,6 +130,9 @@ public function testFromTelegramResult(): void assertTrue($poll->allowsMultipleAnswers); assertTrue($poll->allowsRevoting); + assertTrue($poll->membersOnly); + assertSame(['US', 'GB', 'DE'], $poll->countryCodes); + assertCount(1, $poll->questionEntities); assertSame(35, $poll->questionEntities[0]->length); @@ -114,10 +142,14 @@ public function testFromTelegramResult(): void assertCount(1, $poll->explanationEntities); assertSame(31, $poll->explanationEntities[0]->length); + assertSame(55.7558, $poll->explanationMedia?->location?->latitude); + assertSame(123, $poll->openPeriod); assertSame(456, $poll->closeDate); assertSame('Poll description', $poll->description); assertCount(1, $poll->descriptionEntities); assertSame(4, $poll->descriptionEntities[0]->length); + + assertSame('sticker_file_id', $poll->media?->sticker?->fileId); } } diff --git a/tests/Type/SentGuestMessageTest.php b/tests/Type/SentGuestMessageTest.php new file mode 100644 index 00000000..facb23b4 --- /dev/null +++ b/tests/Type/SentGuestMessageTest.php @@ -0,0 +1,28 @@ +inlineMessageId); + } + + public function testFromTelegramResult(): void + { + $type = (new ObjectFactory())->create(['inline_message_id' => 'inline_msg_456'], null, SentGuestMessage::class); + + assertSame('inline_msg_456', $type->inlineMessageId); + } +} diff --git a/tests/Type/Update/UpdateTest.php b/tests/Type/Update/UpdateTest.php index 56b2754f..fea61912 100644 --- a/tests/Type/Update/UpdateTest.php +++ b/tests/Type/Update/UpdateTest.php @@ -31,6 +31,7 @@ public function testBase(): void assertNull($update->businessMessage); assertNull($update->editedBusinessMessage); assertNull($update->deletedBusinessMessages); + assertNull($update->guestMessage); assertNull($update->messageReaction); assertNull($update->messageReactionCount); assertNull($update->inlineQuery); @@ -131,6 +132,16 @@ public function testFromTelegramResult(): void ], 'message_ids' => [7, 8], ], + 'guest_message' => [ + 'message_id' => 9, + 'date' => 1625150000, + 'chat' => [ + 'id' => 23, + 'type' => 'private', + ], + 'guest_query_id' => 'guest_123', + 'text' => 'Guest message', + ], 'message_reaction' => [ 'chat' => [ 'id' => 23, @@ -228,6 +239,7 @@ public function testFromTelegramResult(): void 'type' => 'regular', 'allows_multiple_answers' => false, 'allows_revoting' => true, + 'members_only' => false, ], 'poll_answer' => [ 'poll_id' => 'poll2', @@ -362,6 +374,8 @@ public function testFromTelegramResult(): void assertSame(5, $update->businessMessage?->messageId); assertSame(6, $update->editedBusinessMessage?->messageId); assertSame('bcid2', $update->deletedBusinessMessages?->businessConnectionId); + assertSame(9, $update->guestMessage?->messageId); + assertSame('guest_123', $update->guestMessage?->guestQueryId); assertSame(79, $update->messageReaction?->messageId); assertSame(80, $update->messageReactionCount?->messageId); assertSame('iqid1', $update->inlineQuery?->id); diff --git a/tests/Type/UserTest.php b/tests/Type/UserTest.php index e6e3c3c4..05a67ae7 100644 --- a/tests/Type/UserTest.php +++ b/tests/Type/UserTest.php @@ -28,6 +28,7 @@ public function testBase(): void assertNull($user->addedToAttachmentMenu); assertNull($user->canJoinGroups); assertNull($user->canReadAllGroupMessages); + assertNull($user->supportsGuestQueries); assertNull($user->supportsInlineQueries); assertNull($user->canConnectToBusiness); assertNull($user->hasMainWebApp); @@ -51,6 +52,7 @@ public function testToRequestArray(): void true, true, true, + true, false, true, true, @@ -69,6 +71,7 @@ public function testToRequestArray(): void 'added_to_attachment_menu' => true, 'can_join_groups' => true, 'can_read_all_group_messages' => true, + 'supports_guest_queries' => true, 'supports_inline_queries' => true, 'can_connect_to_business' => true, 'has_main_web_app' => false, @@ -93,6 +96,7 @@ public function testFromTelegramResult(): void 'added_to_attachment_menu' => true, 'can_join_groups' => true, 'can_read_all_group_messages' => true, + 'supports_guest_queries' => true, 'supports_inline_queries' => true, 'can_connect_to_business' => true, 'has_main_web_app' => false, @@ -112,6 +116,7 @@ public function testFromTelegramResult(): void assertSame(true, $user->addedToAttachmentMenu); assertSame(true, $user->canJoinGroups); assertSame(true, $user->canReadAllGroupMessages); + assertSame(true, $user->supportsGuestQueries); assertSame(true, $user->supportsInlineQueries); assertSame(true, $user->canConnectToBusiness); assertSame(false, $user->hasMainWebApp);