diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index a334bee5..87764e11 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - php: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php: ['8.1', '8.2', '8.3'] services: mariadb: @@ -70,11 +70,15 @@ jobs: run: composer check-code - name: Run test suite - if: ${{ matrix.php != '7.3'}} - run: composer test-cov + run: composer test - - name: Run test suite (with coverage) - if: ${{ matrix.php == '7.3'}} - run: | - composer test-cov - composer test-cov-upload +# - name: Run test suite +# if: ${{ matrix.php != '8.1'}} +# run: composer test-cov + +# - name: Run test suite (with coverage) +# if: ${{ matrix.php == '8.1'}} +# run: | +# wget https://scrutinizer-ci.com/ocular.phar +# composer test-cov +# composer test-cov-upload diff --git a/.scrutinizer.yml b/.scrutinizer.yml index d3e57889..d788c467 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,6 +1,6 @@ build: environment: - php: 7.3.0 + php: 8.1.0 nodes: analysis: tests: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2608bb47..c7bc9263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,12 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c ## [Unreleased] ### Notes -- [:ledger: View file changes][Unreleased] +- [:ledger: View file changes][Unreleased] ∙ [:page_with_curl: DB migration script][unreleased-sql-migration] +- [:exclamation:][unreleased-bc-minimum-php-81] PHP 8.1+ required! ### Added +- Bot API 7.0 (@noplanman, @TiiFuchs) (#1459) ### Changed +- [:exclamation:][unreleased-bc-user-to-users] Various fields have been pluralised from "user" to "users". ### Deprecated ### Removed ### Fixed @@ -634,6 +637,9 @@ Exclamation symbols (:exclamation:) note something of importance e.g. breaking c ### Deprecated - Move `hideKeyboard` to `removeKeyboard`. +[unreleased-sql-migration]: https://github.com/php-telegram-bot/core/tree/develop/utils/db-schema-update/0.82.0-unreleased.sql +[unreleased-bc-unreleased-bc-minimum-php-81]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#minimum-php-81 +[unreleased-bc-unreleased-bc-user-to-users]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#user-to-users [0.82.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.81.0-0.82.0.sql [0.81.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.80.0-0.81.0.sql [0.80.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.79.0-0.80.0.sql diff --git a/README.md b/README.md index 85c13e61..51654d8b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ A Telegram Bot based on the official [Telegram Bot API] [![Dependencies](https://tidelift.com/badges/github/php-telegram-bot/core?style=flat)][Tidelift] [![Total Downloads](https://img.shields.io/packagist/dt/longman/telegram-bot.svg)](https://packagist.org/packages/longman/telegram-bot) [![Downloads Month](https://img.shields.io/packagist/dm/longman/telegram-bot.svg)](https://packagist.org/packages/longman/telegram-bot) -[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D7.3-8892BF.svg)](https://php.net/) +[![Minimum PHP Version](http://img.shields.io/badge/php-%3E%3D8.1-8892BF.svg)](https://php.net/) [![License](https://img.shields.io/packagist/l/longman/telegram-bot.svg)](https://github.com/php-telegram-bot/core/LICENSE) ## Table of Contents @@ -176,7 +176,7 @@ Create *composer.json* file "name": "yourproject/yourproject", "type": "project", "require": { - "php": ">=7.3", + "php": "^8.1", "longman/telegram-bot": "*" } } diff --git a/composer.json b/composer.json index e94888dd..1c421ec9 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ } ], "require": { - "php": "^7.3|^8.0", + "php": "^8.1", "ext-pdo": "*", "ext-curl": "*", "ext-json": "*", @@ -64,7 +64,7 @@ "XDEBUG_MODE=coverage \"vendor/bin/phpunit\" --coverage-clover clover.xml" ], "test-cov-upload": [ - "wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover clover.xml" + "@php ocular.phar code-coverage:upload --format=php-clover clover.xml" ] }, "config": { @@ -75,7 +75,7 @@ }, "extra": { "branch-alias": { - "dev-develop": "0.81.x-dev" + "dev-develop": "0.83.x-dev" } } } diff --git a/src/DB.php b/src/DB.php index 6472d171..cae89be1 100644 --- a/src/DB.php +++ b/src/DB.php @@ -14,11 +14,15 @@ use Longman\TelegramBot\Entities\CallbackQuery; use Longman\TelegramBot\Entities\Chat; +use Longman\TelegramBot\Entities\ChatBoostRemoved; +use Longman\TelegramBot\Entities\ChatBoostUpdated; use Longman\TelegramBot\Entities\ChatJoinRequest; use Longman\TelegramBot\Entities\ChatMemberUpdated; use Longman\TelegramBot\Entities\ChosenInlineResult; use Longman\TelegramBot\Entities\InlineQuery; use Longman\TelegramBot\Entities\Message; +use Longman\TelegramBot\Entities\MessageReactionCountUpdated; +use Longman\TelegramBot\Entities\MessageReactionUpdated; use Longman\TelegramBot\Entities\Payments\PreCheckoutQuery; use Longman\TelegramBot\Entities\Payments\ShippingQuery; use Longman\TelegramBot\Entities\Poll; @@ -74,7 +78,7 @@ public static function initialize( array $credentials, Telegram $telegram, $table_prefix = '', - $encoding = 'utf8mb4' + $encoding = 'utf8mb4', ): PDO { if (empty($credentials)) { throw new TelegramException('MySQL credentials not provided!'); @@ -123,7 +127,7 @@ public static function initialize( public static function externalInitialize( PDO $external_pdo_connection, Telegram $telegram, - string $table_prefix = '' + string $table_prefix = '', ): PDO { if ($external_pdo_connection === null) { throw new TelegramException('MySQL external connection not provided!'); @@ -147,12 +151,16 @@ protected static function defineTables(): void $tables = [ 'callback_query', 'chat', + 'chat_boost_updated', + 'chat_boost_removed', 'chat_join_request', 'chat_member_updated', 'chosen_inline_result', 'edited_message', 'inline_query', 'message', + 'message_reaction', + 'message_reaction_count', 'poll', 'poll_answer', 'pre_checkout_query', @@ -292,12 +300,12 @@ protected static function getTimestamp(?int $unixtime = null): string /** * Convert array of Entity items to a JSON array * - * @todo Find a better way, as json_* functions are very heavy - * * @param array $entities * @param mixed $default * * @return mixed + * @todo Find a better way, as json_* functions are very heavy + * */ public static function entitiesArrayToJson(array $entities, $default = null) { @@ -316,23 +324,6 @@ public static function entitiesArrayToJson(array $entities, $default = null) /** * Insert entry to telegram_update table * - * @param int $update_id - * @param int|null $chat_id - * @param int|null $message_id - * @param int|null $edited_message_id - * @param int|null $channel_post_id - * @param int|null $edited_channel_post_id - * @param string|null $inline_query_id - * @param string|null $chosen_inline_result_id - * @param string|null $callback_query_id - * @param string|null $shipping_query_id - * @param string|null $pre_checkout_query_id - * @param string|null $poll_id - * @param string|null $poll_answer_poll_id - * @param string|null $my_chat_member_updated_id - * @param string|null $chat_member_updated_id - * - * @return bool If the insert was successful * @throws TelegramException */ protected static function insertTelegramUpdate( @@ -342,6 +333,8 @@ protected static function insertTelegramUpdate( ?int $edited_message_id = null, ?int $channel_post_id = null, ?int $edited_channel_post_id = null, + ?string $message_reaction_id = null, + ?string $message_reaction_count_id = null, ?string $inline_query_id = null, ?string $chosen_inline_result_id = null, ?string $callback_query_id = null, @@ -351,10 +344,12 @@ protected static function insertTelegramUpdate( ?string $poll_answer_poll_id = null, ?string $my_chat_member_updated_id = null, ?string $chat_member_updated_id = null, - ?string $chat_join_request_id = null + ?string $chat_join_request_id = null, + ?string $chat_boost_updated_id = null, + ?string $chat_boost_removed_id = null, ): ?bool { - if ($message_id === null && $edited_message_id === null && $channel_post_id === null && $edited_channel_post_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $shipping_query_id === null && $pre_checkout_query_id === null && $poll_id === null && $poll_answer_poll_id === null && $my_chat_member_updated_id === null && $chat_member_updated_id === null && $chat_join_request_id === null) { - throw new TelegramException('message_id, edited_message_id, channel_post_id, edited_channel_post_id, inline_query_id, chosen_inline_result_id, callback_query_id, shipping_query_id, pre_checkout_query_id, poll_id, poll_answer_poll_id, my_chat_member_updated_id, chat_member_updated_id are all null'); + if ($message_id === null && $edited_message_id === null && $channel_post_id === null && $edited_channel_post_id === null && $message_reaction_id === null && $message_reaction_count_id === null && $inline_query_id === null && $chosen_inline_result_id === null && $callback_query_id === null && $shipping_query_id === null && $pre_checkout_query_id === null && $poll_id === null && $poll_answer_poll_id === null && $my_chat_member_updated_id === null && $chat_member_updated_id === null && $chat_join_request_id === null && $chat_boost_updated_id === null && $chat_boost_removed_id === null) { + throw new TelegramException('message_id, edited_message_id, channel_post_id, edited_channel_post_id, message_reaction_id, message_reaction_count_id, inline_query_id, chosen_inline_result_id, callback_query_id, shipping_query_id, pre_checkout_query_id, poll_id, poll_answer_poll_id, my_chat_member_updated_id, chat_member_updated_id, chat_join_request_id, chat_boost_updated_id, chat_boost_removed_id are all null'); } if (!self::isDbConnected()) { @@ -366,16 +361,18 @@ protected static function insertTelegramUpdate( INSERT IGNORE INTO `' . TB_TELEGRAM_UPDATE . '` ( `id`, `chat_id`, `message_id`, `edited_message_id`, - `channel_post_id`, `edited_channel_post_id`, `inline_query_id`, `chosen_inline_result_id`, + `channel_post_id`, `edited_channel_post_id`, `message_reaction_id`, `message_reaction_count_id`, + `inline_query_id`, `chosen_inline_result_id`, `callback_query_id`, `shipping_query_id`, `pre_checkout_query_id`, `poll_id`, `poll_answer_poll_id`, `my_chat_member_updated_id`, `chat_member_updated_id`, - `chat_join_request_id` + `chat_join_request_id`, `chat_boost_updated_id`, `chat_boost_removed_id` ) VALUES ( :id, :chat_id, :message_id, :edited_message_id, - :channel_post_id, :edited_channel_post_id, :inline_query_id, :chosen_inline_result_id, + :channel_post_id, :edited_channel_post_id, :message_reaction_id, :message_reaction_count_id, + :inline_query_id, :chosen_inline_result_id, :callback_query_id, :shipping_query_id, :pre_checkout_query_id, :poll_id, :poll_answer_poll_id, :my_chat_member_updated_id, :chat_member_updated_id, - :chat_join_request_id + :chat_join_request_id, :chat_boost_updated_id, :chat_boost_removed_id ) '); @@ -385,6 +382,8 @@ protected static function insertTelegramUpdate( $sth->bindValue(':edited_message_id', $edited_message_id); $sth->bindValue(':channel_post_id', $channel_post_id); $sth->bindValue(':edited_channel_post_id', $edited_channel_post_id); + $sth->bindValue(':message_reaction_id', $message_reaction_id); + $sth->bindValue(':message_reaction_count_id', $message_reaction_count_id); $sth->bindValue(':inline_query_id', $inline_query_id); $sth->bindValue(':chosen_inline_result_id', $chosen_inline_result_id); $sth->bindValue(':callback_query_id', $callback_query_id); @@ -395,6 +394,8 @@ protected static function insertTelegramUpdate( $sth->bindValue(':my_chat_member_updated_id', $my_chat_member_updated_id); $sth->bindValue(':chat_member_updated_id', $chat_member_updated_id); $sth->bindValue(':chat_join_request_id', $chat_join_request_id); + $sth->bindValue(':chat_boost_updated_id', $chat_boost_updated_id); + $sth->bindValue(':chat_boost_removed_id', $chat_boost_removed_id); return $sth->execute(); } catch (PDOException $e) { @@ -538,12 +539,12 @@ public static function insertChat(Chat $chat, ?string $date = null, ?int $migrat /** * Insert request into database * - * @todo self::$pdo->lastInsertId() - unsafe usage if expected previous insert fails? - * * @param Update $update * * @return bool * @throws TelegramException + * @todo self::$pdo->lastInsertId() - unsafe usage if expected previous insert fails? + * */ public static function insertRequest(Update $update): bool { @@ -556,6 +557,8 @@ public static function insertRequest(Update $update): bool $edited_message_id = null; $channel_post_id = null; $edited_channel_post_id = null; + $message_reaction_id = null; + $message_reaction_count_id = null; $inline_query_id = null; $chosen_inline_result_id = null; $callback_query_id = null; @@ -566,6 +569,8 @@ public static function insertRequest(Update $update): bool $my_chat_member_updated_id = null; $chat_member_updated_id = null; $chat_join_request_id = null; + $chat_boost_updated_id = null; + $chat_boost_removed_id = null; if (($message = $update->getMessage()) && self::insertMessageRequest($message)) { $chat_id = $message->getChat()->getId(); @@ -579,6 +584,14 @@ public static function insertRequest(Update $update): bool } elseif (($edited_channel_post = $update->getEditedChannelPost()) && self::insertEditedMessageRequest($edited_channel_post)) { $chat_id = $edited_channel_post->getChat()->getId(); $edited_channel_post_id = (int) self::$pdo->lastInsertId(); + } elseif (($message_reaction = $update->getMessageReaction()) && self::insertMessageReaction($message_reaction)) { + $chat_id = $message_reaction->getChat()->getId(); + $message_id = $message_reaction->getMessageId(); + $message_reaction_id = self::$pdo->lastInsertId(); + } elseif (($message_reaction_count = $update->getMessageReactionCount()) && self::insertMessageReactionCount($message_reaction_count)) { + $chat_id = $message_reaction_count->getChat()->getId(); + $message_id = $message_reaction_count->getMessageId(); + $message_reaction_count_id = self::$pdo->lastInsertId(); } elseif (($inline_query = $update->getInlineQuery()) && self::insertInlineQueryRequest($inline_query)) { $inline_query_id = $inline_query->getId(); } elseif (($chosen_inline_result = $update->getChosenInlineResult()) && self::insertChosenInlineResultRequest($chosen_inline_result)) { @@ -599,6 +612,10 @@ public static function insertRequest(Update $update): bool $chat_member_updated_id = self::$pdo->lastInsertId(); } elseif (($chat_join_request = $update->getChatJoinRequest()) && self::insertChatJoinRequestRequest($chat_join_request)) { $chat_join_request_id = self::$pdo->lastInsertId(); + } elseif (($chat_boost_updated = $update->getChatBoost()) && self::insertChatBoostUpdatedRequest($chat_boost_updated)) { + $chat_boost_updated_id = self::$pdo->lastInsertId(); + } elseif (($chat_boost_removed = $update->getRemovedChatBoost()) && self::insertChatBoostRemovedRequest($chat_boost_removed)) { + $chat_boost_removed_id = self::$pdo->lastInsertId(); } else { return false; } @@ -610,6 +627,8 @@ public static function insertRequest(Update $update): bool $edited_message_id, $channel_post_id, $edited_channel_post_id, + $message_reaction_id, + $message_reaction_count_id, $inline_query_id, $chosen_inline_result_id, $callback_query_id, @@ -619,10 +638,91 @@ public static function insertRequest(Update $update): bool $poll_answer_poll_id, $my_chat_member_updated_id, $chat_member_updated_id, - $chat_join_request_id + $chat_join_request_id, + $chat_boost_updated_id, + $chat_boost_removed_id, ); } + public static function insertMessageReaction(MessageReactionUpdated $message_reaction): bool + { + if (!self::isDbConnected()) { + return false; + } + + try { + $sth = self::$pdo->prepare(' + INSERT IGNORE INTO `' . TB_MESSAGE_REACTION . '` + (`chat_id`, `message_id`, `user_id`, `actor_chat_id`, `old_reaction`, `new_reaction`, `created_at`) + VALUES + (:chat_id, :message_id, :user_id, :actor_chat_id, :old_reaction, :new_reaction, :created_at) + '); + + $date = self::getTimestamp($message_reaction->getDate()); + $chat_id = null; + $user_id = null; + $actor_chat_id = null; + + if ($chat = $message_reaction->getChat()) { + $chat_id = $chat->getId(); + self::insertChat($chat, $date); + } + if ($user = $message_reaction->getUser()) { + $user_id = $user->getId(); + self::insertUser($user, $date); + } + if ($actor_chat = $message_reaction->getActorChat()) { + $actor_chat_id = $actor_chat->getId(); + self::insertChat($actor_chat, $date); + } + + $sth->bindValue(':chat_id', $chat_id); + $sth->bindValue(':message_id', $message_reaction->getMessageId()); + $sth->bindValue(':user_id', $user_id); + $sth->bindValue(':actor_chat_id', $actor_chat_id); + $sth->bindValue(':old_reaction', self::entitiesArrayToJson($message_reaction->getOldReaction() ?: [])); + $sth->bindValue(':new_reaction', self::entitiesArrayToJson($message_reaction->getNewReaction() ?: [])); + $sth->bindValue(':created_at', $date); + + return $sth->execute(); + } catch (PDOException $e) { + throw new TelegramException($e->getMessage()); + } + } + + public static function insertMessageReactionCount(MessageReactionCountUpdated $message_reaction_count): bool + { + if (!self::isDbConnected()) { + return false; + } + + try { + $sth = self::$pdo->prepare(' + INSERT IGNORE INTO `' . TB_MESSAGE_REACTION_COUNT . '` + (`chat_id`, `message_id`, `reactions`, `created_at`) + VALUES + (:chat_id, :message_id, :reactions, :created_at) + '); + + $date = self::getTimestamp($message_reaction_count->getDate()); + $chat_id = null; + + if ($chat = $message_reaction->getChat()) { + $chat_id = $chat->getId(); + self::insertChat($chat, $date); + } + + $sth->bindValue(':chat_id', $chat_id); + $sth->bindValue(':message_id', $message_reaction_count->getMessageId()); + $sth->bindValue(':reactions', $message_reaction_count->getReactions()); + $sth->bindValue(':created_at', $date); + + return $sth->execute(); + } catch (PDOException $e) { + throw new TelegramException($e->getMessage()); + } + } + /** * Insert inline query request into database * @@ -1059,6 +1159,88 @@ public static function insertChatJoinRequestRequest(ChatJoinRequest $chat_join_r } } + /** + * Insert chat boost updated into database + * + * @param ChatBoostUpdated $chat_boost_updated + * + * @return bool If the insert was successful + * @throws TelegramException + */ + public static function insertChatBoostUpdatedRequest(ChatBoostUpdated $chat_boost_updated): bool + { + if (!self::isDbConnected()) { + return false; + } + + try { + $sth = self::$pdo->prepare(' + INSERT INTO `' . TB_CHAT_BOOST_UPDATED . '` + (`chat_id`, `boost`, `created_at`) + VALUES + (:chat_id, :boost, :created_at) + '); + + $date = self::getTimestamp(); + $chat_id = null; + + if ($chat = $chat_boost_updated->getChat()) { + $chat_id = $chat->getId(); + self::insertChat($chat, $date); + } + + $sth->bindValue(':chat_id', $chat_id); + $sth->bindValue(':boost', $chat_boost_updated->getBoost()); + $sth->bindValue(':created_at', $date); + + return $sth->execute(); + } catch (PDOException $e) { + throw new TelegramException($e->getMessage()); + } + } + + /** + * Insert chat boost removed into database + * + * @param ChatBoostRemoved $chat_boost_removed + * + * @return bool If the insert was successful + * @throws TelegramException + */ + public static function insertChatBoostRemovedRequest(ChatBoostRemoved $chat_boost_removed): bool + { + if (!self::isDbConnected()) { + return false; + } + + try { + $sth = self::$pdo->prepare(' + INSERT INTO `' . TB_CHAT_BOOST_REMOVED . '` + (`chat_id`, `boost_id`, `remove_date`, `source`, `created_at`) + VALUES + (:chat_id, :boost_id, :remove_date, :source, :created_at) + '); + + $date = self::getTimestamp(); + $chat_id = null; + + if ($chat = $chat_boost_removed->getChat()) { + $chat_id = $chat->getId(); + self::insertChat($chat, $date); + } + + $sth->bindValue(':chat_id', $chat_id); + $sth->bindValue(':boost_id', $chat_boost_removed->getBoostId()); + $sth->bindValue(':remove_date', self::getTimestamp($chat_boost_removed->getRemoveDate())); + $sth->bindValue(':source', $chat_boost_removed->getSource()); + $sth->bindValue(':created_at', $date); + + return $sth->execute(); + } catch (PDOException $e) { + throw new TelegramException($e->getMessage()); + } + } + /** * Insert Message request in db * @@ -1135,23 +1317,23 @@ public static function insertMessageRequest(Message $message): bool ( `id`, `user_id`, `chat_id`, `message_thread_id`, `sender_chat_id`, `date`, `forward_from`, `forward_from_chat`, `forward_from_message_id`, `forward_signature`, `forward_sender_name`, `forward_date`, `is_topic_message`, - `reply_to_chat`, `reply_to_message`, `via_bot`, `edit_date`, `media_group_id`, `author_signature`, `text`, `entities`, `caption_entities`, + `reply_to_chat`, `reply_to_message`, `external_reply`, `via_bot`, `link_preview_options`, `edit_date`, `media_group_id`, `author_signature`, `text`, `entities`, `caption_entities`, `audio`, `document`, `animation`, `game`, `photo`, `sticker`, `story`, `video`, `voice`, `video_note`, `caption`, `has_media_spoiler`, `contact`, `location`, `venue`, `poll`, `dice`, `new_chat_members`, `left_chat_member`, `new_chat_title`, `new_chat_photo`, `delete_chat_photo`, `group_chat_created`, `supergroup_chat_created`, `channel_chat_created`, `message_auto_delete_timer_changed`, `migrate_to_chat_id`, `migrate_from_chat_id`, - `pinned_message`, `invoice`, `successful_payment`, `user_shared`, `chat_shared`, `connected_website`, `write_access_allowed`, `passport_data`, `proximity_alert_triggered`, + `pinned_message`, `invoice`, `successful_payment`, `users_shared`, `chat_shared`, `connected_website`, `write_access_allowed`, `passport_data`, `proximity_alert_triggered`, `forum_topic_created`, `forum_topic_edited`, `forum_topic_closed`, `forum_topic_reopened`, `general_forum_topic_hidden`, `general_forum_topic_unhidden`, `video_chat_scheduled`, `video_chat_started`, `video_chat_ended`, `video_chat_participants_invited`, `web_app_data`, `reply_markup` ) VALUES ( :message_id, :user_id, :chat_id, :message_thread_id, :sender_chat_id, :date, :forward_from, :forward_from_chat, :forward_from_message_id, :forward_signature, :forward_sender_name, :forward_date, :is_topic_message, - :reply_to_chat, :reply_to_message, :via_bot, :edit_date, :media_group_id, :author_signature, :text, :entities, :caption_entities, + :reply_to_chat, :reply_to_message, :external_reply, :via_bot, :link_preview_options, :edit_date, :media_group_id, :author_signature, :text, :entities, :caption_entities, :audio, :document, :animation, :game, :photo, :sticker, :story, :video, :voice, :video_note, :caption, :has_media_spoiler, :contact, :location, :venue, :poll, :dice, :new_chat_members, :left_chat_member, :new_chat_title, :new_chat_photo, :delete_chat_photo, :group_chat_created, :supergroup_chat_created, :channel_chat_created, :message_auto_delete_timer_changed, :migrate_to_chat_id, :migrate_from_chat_id, - :pinned_message, :invoice, :successful_payment, :user_shared, :chat_shared, :connected_website, :write_access_allowed, :passport_data, :proximity_alert_triggered, + :pinned_message, :invoice, :successful_payment, :users_shared, :chat_shared, :connected_website, :write_access_allowed, :passport_data, :proximity_alert_triggered, :forum_topic_created, :forum_topic_edited, :forum_topic_closed, :forum_topic_reopened, :general_forum_topic_hidden, :general_forum_topic_unhidden, :video_chat_scheduled, :video_chat_started, :video_chat_ended, :video_chat_participants_invited, :web_app_data, :reply_markup ) @@ -1188,8 +1370,10 @@ public static function insertMessageRequest(Message $message): bool } $sth->bindValue(':reply_to_chat', $reply_to_chat_id); $sth->bindValue(':reply_to_message', $reply_to_message_id); + $sth->bindValue(':external_reply', $message->getExternalReply()); $sth->bindValue(':via_bot', $via_bot_id); + $sth->bindValue(':link_preview_options', $message->getLinkPreviewOptions()); $sth->bindValue(':edit_date', self::getTimestamp($message->getEditDate())); $sth->bindValue(':media_group_id', $message->getMediaGroupId()); $sth->bindValue(':author_signature', $message->getAuthorSignature()); @@ -1227,7 +1411,7 @@ public static function insertMessageRequest(Message $message): bool $sth->bindValue(':pinned_message', $message->getPinnedMessage()); $sth->bindValue(':invoice', $message->getInvoice()); $sth->bindValue(':successful_payment', $message->getSuccessfulPayment()); - $sth->bindValue(':user_shared', $message->getUserShared()); + $sth->bindValue(':users_shared', $message->getUsersShared()); $sth->bindValue(':chat_shared', $message->getChatShared()); $sth->bindValue(':connected_website', $message->getConnectedWebsite()); $sth->bindValue(':write_access_allowed', $message->getWriteAccessAllowed()); diff --git a/src/Entities/CallbackQuery.php b/src/Entities/CallbackQuery.php index e65f6bf2..d5fd51d7 100644 --- a/src/Entities/CallbackQuery.php +++ b/src/Entities/CallbackQuery.php @@ -11,6 +11,8 @@ namespace Longman\TelegramBot\Entities; +use Longman\TelegramBot\Entities\Message\Factory as MaybeInaccessibleMessageFactory; +use Longman\TelegramBot\Entities\Message\MaybeInaccessibleMessage; use Longman\TelegramBot\Request; /** @@ -18,13 +20,13 @@ * * @link https://core.telegram.org/bots/api#callbackquery * - * @method string getId() Unique identifier for this query - * @method User getFrom() Sender - * @method Message getMessage() Optional. Message with the callback button that originated the query. Note that message content and message date will not be available if the message is too old - * @method string getInlineMessageId() Optional. Identifier of the message sent via the bot in inline mode, that originated the query - * @method string getChatInstance() Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games. - * @method string getData() Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field - * @method string getGameShortName() Optional. Short name of a Game to be returned, serves as the unique identifier for the game + * @method string getId() Unique identifier for this query + * @method User getFrom() Sender + * @method MaybeInaccessibleMessage getMessage() Optional. Message with the callback button that originated the query. Note that message content and message date will not be available if the message is too old + * @method string getInlineMessageId() Optional. Identifier of the message sent via the bot in inline mode, that originated the query + * @method string getChatInstance() Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent. Useful for high scores in games. + * @method string getData() Data associated with the callback button. Be aware that a bad client can send arbitrary data in this field + * @method string getGameShortName() Optional. Short name of a Game to be returned, serves as the unique identifier for the game */ class CallbackQuery extends Entity { @@ -35,7 +37,7 @@ protected function subEntities(): array { return [ 'from' => User::class, - 'message' => Message::class, + 'message' => MaybeInaccessibleMessageFactory::class, ]; } diff --git a/src/Entities/Chat.php b/src/Entities/Chat.php index 97798243..8a46c519 100644 --- a/src/Entities/Chat.php +++ b/src/Entities/Chat.php @@ -11,8 +11,11 @@ namespace Longman\TelegramBot\Entities; +use Longman\TelegramBot\Entities\ReactionType\Factory as ReactionTypeFactory; +use Longman\TelegramBot\Entities\ReactionType\ReactionType; + /** - * Class Chat + * This object represents a chat. * * @link https://core.telegram.org/bots/api#chat * @@ -25,8 +28,13 @@ * @method string getFirstName() Optional. First name of the other party in a private chat * @method string getLastName() Optional. Last name of the other party in a private chat * @method bool getIsForum() Optional. True, if the supergroup chat is a forum (has topics enabled) + * @method int getAccentColorId() Identifier of the accent color for the chat name and backgrounds of the chat photo, reply header, and link preview. See accent colors for more details. * @method ChatPhoto getPhoto() Optional. Chat photo. Returned only in getChat. * @method string[] getActiveUsernames() Optional. If non-empty, the list of all active chat usernames; for private chats, supergroups and channels. Returned only in getChat. + * @method ReactionType[] getAvailableReactions() Optional. List of available reactions allowed in the chat. If omitted, then all emoji reactions are allowed. Returned only in getChat. + * @method string getBackgroundCustomEmojiId() Optional. Custom emoji identifier of the emoji chosen by the chat for the reply header and link preview background + * @method int getProfileAccentColorId() Optional. Identifier of the accent color for the chat's profile background. See profile accent colors for more details. + * @method string getProfileBackgroundCustomEmojiId() Optional. Custom emoji identifier of the emoji chosen by the chat for its profile background * @method string getEmojiStatusCustomEmojiId() Optional. Custom emoji identifier of emoji status of the other party in a private chat. Returned only in getChat. * @method int getEmojiStatusExpirationDate() Optional. Expiration date of the emoji status of the other party in a private chat in Unix time, if any. Returned only in getChat. * @method string getBio() Optional. Bio of the other party in a private chat. Returned only in getChat. @@ -43,6 +51,7 @@ * @method bool getHasAggressiveAntiSpamEnabled() Optional. True, if aggressive anti-spam checks are enabled in the supergroup. The field is only available to chat administrators. Returned only in getChat. * @method bool getHasHiddenMembers() Optional. True, if non-administrators can only get the list of bots and administrators in the chat. Returned only in getChat. * @method bool getHasProtectedContent() Optional. True, if messages from the chat can't be forwarded to other chats. Returned only in getChat. + * @method bool getHasVisibleHistory() Optional. True, if new chat members will have access to old messages; available only to chat administrators * @method string getStickerSetName() Optional. For supergroups, name of group sticker set. Returned only in getChat. * @method bool getCanSetStickerSet() Optional. True, if the bot can change the group sticker set. Returned only in getChat. * @method int getLinkedChatId() Optional. Unique identifier for the linked chat. Returned only in getChat. @@ -56,14 +65,15 @@ class Chat extends Entity protected function subEntities(): array { return [ - 'photo' => ChatPhoto::class, - 'pinned_message' => Message::class, - 'permissions' => ChatPermissions::class, - 'location' => ChatLocation::class, + 'photo' => ChatPhoto::class, + 'available_reactions' => [ReactionTypeFactory::class], + 'pinned_message' => Message::class, + 'permissions' => ChatPermissions::class, + 'location' => ChatLocation::class, ]; } - public function __construct($data) + public function __construct(array $data) { parent::__construct($data); diff --git a/src/Entities/ChatBoost.php b/src/Entities/ChatBoost.php new file mode 100644 index 00000000..adfbddb0 --- /dev/null +++ b/src/Entities/ChatBoost.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +use Longman\TelegramBot\Entities\ChatBoostSource\Factory as ChatBoostSourceFactory; + +/** + * This object contains information about a chat boost. + * + * @link https://core.telegram.org/bots/api#chatboost + * + * @method string getBoostId() Unique identifier of the boost + * @method int getAddDate() Point in time (Unix timestamp) when the chat was boosted + * @method int getExpirationDate() Point in time (Unix timestamp) when the boost will automatically expire, unless the booster's Telegram Premium subscription is prolonged + * @method ChatBoostSource getSource() Source of the added boost + */ +class ChatBoost extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'source' => ChatBoostSourceFactory::class, + ]; + } +} diff --git a/src/Entities/ChatBoostRemoved.php b/src/Entities/ChatBoostRemoved.php new file mode 100644 index 00000000..2b893cca --- /dev/null +++ b/src/Entities/ChatBoostRemoved.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +use Longman\TelegramBot\Entities\ChatBoostSource\Factory as ChatBoostSourceFactory; + +/** + * This object represents a boost removed from a chat. + * + * @link https://core.telegram.org/bots/api#chatboostremoved + * + * @method Chat getChat() Chat which was boosted + * @method string getBoostId() Unique identifier of the boost + * @method int getRemoveDate() Point in time (Unix timestamp) when the boost was removed + * @method ChatBoostSource getSource() Source of the removed boost + */ +class ChatBoostRemoved extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'chat' => Chat::class, + 'source' => ChatBoostSourceFactory::class, + ]; + } +} diff --git a/src/Entities/ChatBoostSource/ChatBoostSource.php b/src/Entities/ChatBoostSource/ChatBoostSource.php new file mode 100644 index 00000000..dae048a9 --- /dev/null +++ b/src/Entities/ChatBoostSource/ChatBoostSource.php @@ -0,0 +1,16 @@ + User::class, + ]; + } +} diff --git a/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php b/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php new file mode 100644 index 00000000..7e405480 --- /dev/null +++ b/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php @@ -0,0 +1,29 @@ + User::class, + ]; + } +} diff --git a/src/Entities/ChatBoostSource/ChatBoostSourceNotImplemented.php b/src/Entities/ChatBoostSource/ChatBoostSourceNotImplemented.php new file mode 100644 index 00000000..e045fcc0 --- /dev/null +++ b/src/Entities/ChatBoostSource/ChatBoostSourceNotImplemented.php @@ -0,0 +1,15 @@ + User::class, + ]; + } +} diff --git a/src/Entities/ChatBoostSource/Factory.php b/src/Entities/ChatBoostSource/Factory.php new file mode 100644 index 00000000..11e5af78 --- /dev/null +++ b/src/Entities/ChatBoostSource/Factory.php @@ -0,0 +1,24 @@ + ChatBoostSourcePremium::class, + 'gift_code' => ChatBoostSourceGiftCode::class, + 'giveaway' => ChatBoostSourceGiveaway::class, + ]; + + if (!isset($type[$data['source'] ?? ''])) { + return new ChatBoostSourceNotImplemented($data, $bot_username); + } + + $class = $type[$data['source']]; + return new $class($data, $bot_username); + } +} diff --git a/src/Entities/ChatBoostUpdated.php b/src/Entities/ChatBoostUpdated.php new file mode 100644 index 00000000..5228b957 --- /dev/null +++ b/src/Entities/ChatBoostUpdated.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +/** + * This object represents a boost added to a chat or changed. + * + * @link https://core.telegram.org/bots/api#chatboostupdated + * + * @method Chat getChat() Chat which was boosted + * @method ChatBoost getBoost() Information about the chat boost + */ +class ChatBoostUpdated extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'chat' => Chat::class, + 'chat_boost' => ChatBoost::class, + ]; + } +} diff --git a/src/Entities/ExternalReplyInfo.php b/src/Entities/ExternalReplyInfo.php new file mode 100644 index 00000000..e879973d --- /dev/null +++ b/src/Entities/ExternalReplyInfo.php @@ -0,0 +1,74 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +/** + * This object contains information about a message that is being replied to, which may come from another chat or forum topic. + * + * @link https://core.telegram.org/bots/api#externalreplyinfo + * + * @method MessageOrigin getOrigin() Origin of the message replied to by the given message + * @method Chat getChat() Optional. Chat the original message belongs to. Available only if the chat is a supergroup or a channel. + * @method int getMessageId() Optional. Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel. + * @method LinkPreviewOptions getLinkPreviewOptions() Optional. Options used for link preview generation for the original message, if it is a text message + * @method Animation getAnimation() Optional. Message is an animation, information about the animation + * @method Audio getAudio() Optional. Message is an audio file, information about the file + * @method Document getDocument() Optional. Message is a general file, information about the file + * @method PhotoSize[] getPhoto() Optional. Message is a photo, available sizes of the photo + * @method Sticker getSticker() Optional. Message is a sticker, information about the sticker + * @method Story getStory() Optional. Message is a forwarded story + * @method Video getVideo() Optional. Message is a video, information about the video + * @method VideoNote getVideoNote() Optional. Message is a video note, information about the video message + * @method Voice getVoice() Optional. Message is a voice message, information about the file + * @method bool getHasMediaSpoiler() Optional. True, if the message media is covered by a spoiler animation + * @method Contact getContact() Optional. Message is a shared contact, information about the contact + * @method Dice getDice() Optional. Message is a dice with random value + * @method Game getGame() Optional. Message is a game, information about the game. More about games » + * @method Giveaway getGiveaway() Optional. Message is a scheduled giveaway, information about the giveaway + * @method GiveawayWinners getGiveawayWinners() Optional. A giveaway with public winners was completed + * @method Invoice getInvoice() Optional. Message is an invoice for a payment, information about the invoice. More about payments » + * @method Location getLocation() Optional. Message is a shared location, information about the location + * @method Poll getPoll() Optional. Message is a native poll, information about the poll + * @method Venue getVenue() Optional. Message is a venue, information about the venue + */ +class ExternalReplyInfo extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'origin' => MessageOrigin::class, + 'chat' => Chat::class, + 'link_preview_options' => LinkPreviewOptions::class, + 'animation' => Animation::class, + 'audio' => Audio::class, + 'document' => Document::class, + 'photo' => [PhotoSize::class], + 'sticker' => Sticker::class, + 'story' => Story::class, + 'video' => Video::class, + 'video_note' => VideoNote::class, + 'voice' => Voice::class, + 'contact' => Contact::class, + 'dice' => Dice::class, + 'game' => Game::class, + 'giveaway' => Giveaway::class, + 'giveaway_winners' => GiveawayWinners::class, + 'invoice' => Invoice::class, + 'location' => Location::class, + 'poll' => Poll::class, + 'venue' => Venue::class, + ]; + } +} diff --git a/src/Entities/Giveaway/Giveaway.php b/src/Entities/Giveaway/Giveaway.php new file mode 100644 index 00000000..bd7fe931 --- /dev/null +++ b/src/Entities/Giveaway/Giveaway.php @@ -0,0 +1,30 @@ + getChats() The list of chats which the user must join to participate in the giveaway + * @method int getWinnersSelectionDate() Point in time (Unix timestamp) when winners of the giveaway will be selected + * @method int getWinnerCount() The number of users which are supposed to be selected as winners of the giveaway + * @method bool getOnlyNewMembers() Optional. True, if only users who join the chats after the giveaway started should be eligible to win + * @method bool getHasPublicWinners() Optional. True, if the list of giveaway winners will be visible to everyone + * @method string getPrizeDescription() Optional. Description of additional giveaway prize + * @method array getCountryCodes() Optional. A list of two-letter ISO 3166-1 alpha-2 country codes indicating the countries from which eligible users for the giveaway must come. If empty, then all users can participate in the giveaway. Users with a phone number that was bought on Fragment can always participate in giveaways. + * @method int getPremiumSubscriptionMonthCount() Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for + */ +class Giveaway extends Entity +{ + protected function subEntities(): array + { + return [ + 'chats' => [Chat::class], + ]; + } +} diff --git a/src/Entities/Giveaway/GiveawayCompleted.php b/src/Entities/Giveaway/GiveawayCompleted.php new file mode 100644 index 00000000..29450072 --- /dev/null +++ b/src/Entities/Giveaway/GiveawayCompleted.php @@ -0,0 +1,25 @@ + Message::class, + ]; + } +} diff --git a/src/Entities/Giveaway/GiveawayCreated.php b/src/Entities/Giveaway/GiveawayCreated.php new file mode 100644 index 00000000..c4ec8dee --- /dev/null +++ b/src/Entities/Giveaway/GiveawayCreated.php @@ -0,0 +1,10 @@ + getWinners() List of up to 100 winners of the giveaway + * @method int getAdditionalChatCount() Optional. The number of other chats the user had to join in order to be eligible for the giveaway + * @method int getPremiumSubscriptionMonthCount() Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for + * @method int getUnclaimedPrizeCount() Optional. Number of undistributed prizes + * @method bool getOnlyNewMembers() Optional. True, if only users who had joined the chats after the giveaway started were eligible to win + * @method bool getWasRefunded() Optional. True, if the giveaway was canceled because the payment for it was refunded + * @method string getPrizeDescription() Optional. Description of additional giveaway prize + */ +class GiveawayWinners extends Entity +{ + protected function subEntities(): array + { + return [ + 'chat' => Chat::class, + 'winners' => [User::class], + ]; + } +} diff --git a/src/Entities/InputMessageContent/InputTextMessageContent.php b/src/Entities/InputMessageContent/InputTextMessageContent.php index ebb47545..574f93af 100644 --- a/src/Entities/InputMessageContent/InputTextMessageContent.php +++ b/src/Entities/InputMessageContent/InputTextMessageContent.php @@ -14,27 +14,19 @@ use Longman\TelegramBot\Entities\InlineQuery\InlineEntity; /** - * Class InputTextMessageContent + * Represents the content of a text message to be sent as the result of an inline query. * * @link https://core.telegram.org/bots/api#inputtextmessagecontent * - * - * $data = [ - * 'message_text' => '', - * 'parse_mode' => '', - * 'disable_web_page_preview' => true, - * ]; - * + * @method string getMessageText() Text of the message to be sent, 1-4096 characters. + * @method string getParseMode() Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. + * @method MessageEntity[] getEntities() Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode + * @method LinkPreviewOptions getLinkPreviewOptions() Optional. Link preview generation options for the message * - * @method string getMessageText() Text of the message to be sent, 1-4096 characters. - * @method string getParseMode() Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. - * @method MessageEntity[] getEntities() Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode - * @method bool getDisableWebPagePreview() Optional. Disables link previews for links in the sent message - * - * @method $this setMessageText(string $message_text) Text of the message to be sent, 1-4096 characters. - * @method $this setParseMode(string $parse_mode) Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. - * @method $this setEntities(array $entities) Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode - * @method $this setDisableWebPagePreview(bool $disable_web_page_preview) Optional. Disables link previews for links in the sent message + * @method $this setMessageText(string $message_text) Text of the message to be sent, 1-4096 characters. + * @method $this setParseMode(string $parse_mode) Optional. Send Markdown or HTML, if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your bot's message. + * @method $this setEntities(array $entities) Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode + * @method $this setLinkPreviewOptions(LinkPreviewOptions $link_preview_options) Optional. Link preview generation options for the message */ class InputTextMessageContent extends InlineEntity implements InputMessageContent { diff --git a/src/Entities/KeyboardButton.php b/src/Entities/KeyboardButton.php index d735a32d..25cc7f04 100644 --- a/src/Entities/KeyboardButton.php +++ b/src/Entities/KeyboardButton.php @@ -11,8 +11,6 @@ namespace Longman\TelegramBot\Entities; -use Longman\TelegramBot\Exception\TelegramException; - /** * Class KeyboardButton * @@ -20,28 +18,28 @@ * * @link https://core.telegram.org/bots/api#keyboardbutton * - * @property KeyboardButtonRequestUser $request_user - * @property KeyboardButtonRequestChat $request_chat - * @property bool $request_contact - * @property bool $request_location - * @property KeyboardButtonPollType $request_poll - * @property WebAppInfo $web_app + * @property KeyboardButtonRequestUsers $request_users + * @property KeyboardButtonRequestChat $request_chat + * @property bool $request_contact + * @property bool $request_location + * @property KeyboardButtonPollType $request_poll + * @property WebAppInfo $web_app * - * @method string getText() Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed - * @method KeyboardButtonRequestUser getRequestUser() Optional. If specified, pressing the button will open a list of suitable users. Tapping on any user will send their identifier to the bot in a “user_shared” service message. Available in private chats only. - * @method KeyboardButtonRequestChat getRequestChat() Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only. - * @method bool getRequestContact() Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only - * @method bool getRequestLocation() Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only - * @method KeyboardButtonPollType getRequestPoll() Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only - * @method WebAppInfo getWebApp() Optional. If specified, the described Web App will be launched when the button is pressed. The Web App will be able to send a “web_app_data” service message. Available in private chats only. + * @method string getText() Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed + * @method KeyboardButtonRequestUsers getRequestUsers() Optional. If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a “users_shared” service message. Available in private chats only. + * @method KeyboardButtonRequestChat getRequestChat() Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only. + * @method bool getRequestContact() Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only + * @method bool getRequestLocation() Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only + * @method KeyboardButtonPollType getRequestPoll() Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only + * @method WebAppInfo getWebApp() Optional. If specified, the described Web App will be launched when the button is pressed. The Web App will be able to send a “web_app_data” service message. Available in private chats only. * - * @method $this setText(string $text) Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed - * @method $this setRequestUser(KeyboardButtonRequestUser $request_user) Optional. If specified, pressing the button will open a list of suitable users. Tapping on any user will send their identifier to the bot in a “user_shared” service message. Available in private chats only. - * @method $this setRequestChat(KeyboardButtonRequestChat $request_chat) Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only. - * @method $this setRequestContact(bool $request_contact) Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only - * @method $this setRequestLocation(bool $request_location) Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only - * @method $this setRequestPoll(KeyboardButtonPollType $request_poll) Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only - * @method $this setWebApp(WebAppInfo $web_app) Optional. If specified, the described Web App will be launched when the button is pressed. The Web App will be able to send a “web_app_data” service message. Available in private chats only. + * @method $this setText(string $text) Text of the button. If none of the optional fields are used, it will be sent to the bot as a message when the button is pressed + * @method $this setRequestUsers(KeyboardButtonRequestUsers $request_users) Optional. If specified, pressing the button will open a list of suitable users. Identifiers of selected users will be sent to the bot in a “users_shared” service message. Available in private chats only. + * @method $this setRequestChat(KeyboardButtonRequestChat $request_chat) Optional. If specified, pressing the button will open a list of suitable chats. Tapping on a chat will send its identifier to the bot in a “chat_shared” service message. Available in private chats only. + * @method $this setRequestContact(bool $request_contact) Optional. If True, the user's phone number will be sent as a contact when the button is pressed. Available in private chats only + * @method $this setRequestLocation(bool $request_location) Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only + * @method $this setRequestPoll(KeyboardButtonPollType $request_poll) Optional. If specified, the user will be asked to create a poll and send it to the bot when the button is pressed. Available in private chats only + * @method $this setWebApp(WebAppInfo $web_app) Optional. If specified, the described Web App will be launched when the button is pressed. The Web App will be able to send a “web_app_data” service message. Available in private chats only. */ class KeyboardButton extends Entity { @@ -59,10 +57,10 @@ public function __construct($data) protected function subEntities(): array { return [ - 'request_user' => KeyboardButtonRequestUser::class, - 'request_chat' => KeyboardButtonRequestChat::class, - 'request_poll' => KeyboardButtonPollType::class, - 'web_app' => WebAppInfo::class, + 'request_users' => KeyboardButtonRequestUsers::class, + 'request_chat' => KeyboardButtonRequestChat::class, + 'request_poll' => KeyboardButtonPollType::class, + 'web_app' => WebAppInfo::class, ]; } @@ -84,8 +82,8 @@ public static function couldBe(array $data): bool public function __call($method, $args) { // Only 1 of these can be set, so clear the others when setting a new one. - if (in_array($method, ['setRequestUser', 'setRequestChat', 'setRequestContact', 'setRequestLocation', 'setRequestPoll', 'setWebApp'], true)) { - unset($this->reqest_user, $this->request_chat, $this->request_contact, $this->request_location, $this->request_poll, $this->web_app); + if (in_array($method, ['setRequestUsers', 'setRequestChat', 'setRequestContact', 'setRequestLocation', 'setRequestPoll', 'setWebApp'], true)) { + unset($this->request_users, $this->request_chat, $this->request_contact, $this->request_location, $this->request_poll, $this->web_app); } return parent::__call($method, $args); diff --git a/src/Entities/KeyboardButtonRequestUser.php b/src/Entities/KeyboardButtonRequestUsers.php similarity index 72% rename from src/Entities/KeyboardButtonRequestUser.php rename to src/Entities/KeyboardButtonRequestUsers.php index 6ba69f62..945bafa9 100644 --- a/src/Entities/KeyboardButtonRequestUser.php +++ b/src/Entities/KeyboardButtonRequestUsers.php @@ -3,21 +3,21 @@ namespace Longman\TelegramBot\Entities; /** - * Class KeyboardButtonRequestUser + * This object defines the criteria used to request suitable users. The identifiers of the selected users will be shared with the bot when the corresponding button is pressed. * - * This entity defines the criteria used to request a suitable user. The identifier of the selected user will be shared with the bot when the corresponding button is pressed. - * - * @link https://core.telegram.org/bots/api#keyboardbuttonrequestuser + * @link https://core.telegram.org/bots/api#keyboardbuttonrequestusers * * @method int getRequestId() Signed 32-bit identifier of the request, which will be received back in the UserShared object. Must be unique within the message * @method bool getUserIsBot() Optional. Pass True to request a bot, pass False to request a regular user. If not specified, no additional restrictions are applied. * @method bool getUserIsPremium() Optional. Pass True to request a premium user, pass False to request a non-premium user. If not specified, no additional restrictions are applied. + * @method int getMaxQuantity() Optional. The maximum number of users to be selected; 1-10. Defaults to 1. * * @method $this setRequestId(int $request_id) Signed 32-bit identifier of the request, which will be received back in the UserShared object. Must be unique within the message * @method $this setUserIsBot(bool $user_is_bot) Optional. Pass True to request a bot, pass False to request a regular user. If not specified, no additional restrictions are applied. * @method $this setUserIsPremium(bool $user_is_premium) Optional. Pass True to request a premium user, pass False to request a non-premium user. If not specified, no additional restrictions are applied. + * @method int setMaxQuantity(int $set_max_quantity) Optional. The maximum number of users to be selected; 1-10. Defaults to 1. */ -class KeyboardButtonRequestUser extends Entity +class KeyboardButtonRequestUsers extends Entity { } diff --git a/src/Entities/LinkPreviewOptions.php b/src/Entities/LinkPreviewOptions.php new file mode 100644 index 00000000..7838e86b --- /dev/null +++ b/src/Entities/LinkPreviewOptions.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +/** + * Describes the options used for link preview generation. + * + * @link https://core.telegram.org/bots/api#linkpreviewoptions + * + * @method bool getIsDisabled() Optional. True, if the link preview is disabled + * @method string getUrl() Optional. URL to use for the link preview. If empty, then the first URL found in the message text will be used + * @method bool getPreferSmallMedia() Optional. True, if the media in the link preview is supposed to be shrunk; ignored if the URL isn't explicitly specified or media size change isn't supported for the preview + * @method bool getPreferLargeMedia() Optional. True, if the media in the link preview is supposed to be enlarged; ignored if the URL isn't explicitly specified or media size change isn't supported for the preview + * @method bool getShowAboveText() Optional. True, if the link preview must be shown above the message text; otherwise, the link preview will be shown below the message text * + */ +class LinkPreviewOptions extends Entity +{ + +} diff --git a/src/Entities/Message.php b/src/Entities/Message.php index 58f36b43..8f771a30 100644 --- a/src/Entities/Message.php +++ b/src/Entities/Message.php @@ -12,6 +12,14 @@ namespace Longman\TelegramBot\Entities; use Longman\TelegramBot\Entities\Games\Game; +use Longman\TelegramBot\Entities\Giveaway\Giveaway; +use Longman\TelegramBot\Entities\Giveaway\GiveawayCompleted; +use Longman\TelegramBot\Entities\Giveaway\GiveawayCreated; +use Longman\TelegramBot\Entities\Giveaway\GiveawayWinners; +use Longman\TelegramBot\Entities\Message\Factory as MaybeInaccessibleMessageFactory; +use Longman\TelegramBot\Entities\Message\MaybeInaccessibleMessage; +use Longman\TelegramBot\Entities\MessageOrigin\Factory as MessageOriginFactory; +use Longman\TelegramBot\Entities\MessageOrigin\MessageOrigin; use Longman\TelegramBot\Entities\Payments\Invoice; use Longman\TelegramBot\Entities\Payments\SuccessfulPayment; use Longman\TelegramBot\Entities\TelegramPassport\PassportData; @@ -35,20 +43,18 @@ * @method Chat getSenderChat() Optional. Sender of the message, sent on behalf of a chat. The channel itself for channel messages. The supergroup itself for messages from anonymous group administrators. The linked channel for messages automatically forwarded to the discussion group * @method int getDate() Date the message was sent in Unix time * @method Chat getChat() Conversation the message belongs to - * @method User getForwardFrom() Optional. For forwarded messages, sender of the original message - * @method Chat getForwardFromChat() Optional. For messages forwarded from a channel, information about the original channel - * @method int getForwardFromMessageId() Optional. For forwarded channel posts, identifier of the original message in the channel - * @method string getForwardSignature() Optional. For messages forwarded from channels, signature of the post author if present - * @method string getForwardSenderName() Optional. Sender's name for messages forwarded from users who disallow adding a link to their account in forwarded messages - * @method int getForwardDate() Optional. For forwarded messages, date the original message was sent in Unix time + * @method MessageOrigin getForwardOrigin() Optional. Information about the original message for forwarded messages * @method bool getIsTopicMessage() Optional. True, if the message is sent to a forum topic * @method bool getIsAutomaticForward() Optional. True, if the message is a channel post that was automatically forwarded to the connected discussion group * @method ReplyToMessage getReplyToMessage() Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply. + * @method ExternalReplyInfo getExternalReply() Optional. Information about the message that is being replied to, which may come from another chat or forum topic + * @method TextQuote getQuote() Optional. For replies that quote part of the original message, the quoted part of the message * @method User getViaBot() Optional. Bot through which the message was sent * @method int getEditDate() Optional. Date the message was last edited in Unix time * @method bool getHasProtectedContent() Optional. True, if the message can't be forwarded * @method string getMediaGroupId() Optional. The unique identifier of a media message group this message belongs to * @method string getAuthorSignature() Optional. Signature of the post author for messages in channels + * @method LinkPreviewOptions getLinkPreviewOptions() Optional. Options used for link preview generation for the message, if it is a text message and link preview options were changed * @method MessageEntity[] getEntities() Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text * @method MessageEntity[] getCaptionEntities() Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption * @method Audio getAudio() Optional. Message is an audio file, information about the file @@ -79,10 +85,10 @@ * @method bool getChannelChatCreated() Optional. Service message: the channel has been created. This field can't be received in a message coming through updates, because bot can’t be a member of a channel when it is created. It can only be found in reply_to_message if someone replies to a very first message in a channel. * @method int getMigrateToChatId() Optional. The group has been migrated to a supergroup with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. * @method int getMigrateFromChatId() Optional. The supergroup has been migrated from a group with the specified identifier. This number may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier. - * @method Message getPinnedMessage() Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply. + * @method MaybeInaccessibleMessage getPinnedMessage() Optional. Specified message was pinned. Note that the Message object in this field will not contain further reply_to_message fields even if it is itself a reply. * @method Invoice getInvoice() Optional. Message is an invoice for a payment, information about the invoice. * @method SuccessfulPayment getSuccessfulPayment() Optional. Message is a service message about a successful payment, information about the payment. - * @method UserShared getUserShared() Optional. Service message: a user was shared with the bot + * @method UsersShared getUsersShared() Optional. Service message: users were shared with the bot * @method ChatShared getChatShared() Optional. Service message: a chat was shared with the bot * @method string getConnectedWebsite() Optional. The domain name of the website on which the user has logged in. * @method WriteAccessAllowed getWriteAccessAllowed() Optional. Service message: the user allowed the bot added to the attachment menu to write messages @@ -94,6 +100,10 @@ * @method ForumTopicReopened getForumTopicReopened() Optional. Service message: forum topic reopened * @method GeneralForumTopicHidden getGeneralForumTopicHidden() Optional. Service message: the 'General' forum topic hidden * @method GeneralForumTopicUnhidden getGeneralForumTopicUnhidden() Optional. Service message: the 'General' forum topic unhidden + * @method GiveawayCreated getGiveawayCreated() Optional. Service message: a scheduled giveaway was created + * @method Giveaway getGiveaway() Optional. The message is a scheduled giveaway message + * @method GiveawayWinners getGiveawayWinners() Optional. A giveaway with public winners was completed + * @method GiveawayCompleted getGiveawayCompleted() Optional. Service message: a giveaway without public winners was completed * @method VideoChatScheduled getVideoChatScheduled() Optional. Service message: voice chat scheduled * @method VideoChatStarted getVideoChatStarted() Optional. Service message: voice chat started * @method VideoChatEnded getVideoChatEnded() Optional. Service message: voice chat ended @@ -101,7 +111,7 @@ * @method WebAppData getWebAppData() Optional. Service message: data sent by a Web App * @method InlineKeyboard getReplyMarkup() Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons. */ -class Message extends Entity +class Message extends Entity implements MaybeInaccessibleMessage { /** * {@inheritdoc} @@ -112,10 +122,12 @@ protected function subEntities(): array 'from' => User::class, 'sender_chat' => Chat::class, 'chat' => Chat::class, - 'forward_from' => User::class, - 'forward_from_chat' => Chat::class, + 'forward_origin' => MessageOriginFactory::class, 'reply_to_message' => ReplyToMessage::class, + 'external_reply' => ExternalReplyInfo::class, + 'quote' => TextQuote::class, 'via_bot' => User::class, + 'link_preview_options' => LinkPreviewOptions::class, 'entities' => [MessageEntity::class], 'animation' => Animation::class, 'audio' => Audio::class, @@ -137,10 +149,10 @@ protected function subEntities(): array 'left_chat_member' => User::class, 'new_chat_photo' => [PhotoSize::class], 'message_auto_delete_timer_changed' => MessageAutoDeleteTimerChanged::class, - 'pinned_message' => __CLASS__, + 'pinned_message' => MaybeInaccessibleMessageFactory::class, 'invoice' => Invoice::class, 'successful_payment' => SuccessfulPayment::class, - 'user_shared' => UserShared::class, + 'users_shared' => UsersShared::class, 'chat_shared' => ChatShared::class, 'write_access_allowed' => WriteAccessAllowed::class, 'passport_data' => PassportData::class, @@ -151,6 +163,10 @@ protected function subEntities(): array 'forum_topic_reopened' => ForumTopicReopened::class, 'general_forum_topic_hidden' => GeneralForumTopicHidden::class, 'general_forum_topic_unhidden' => GeneralForumTopicUnhidden::class, + 'giveaway_created' => GiveawayCreated::class, + 'giveaway' => Giveaway::class, + 'giveaway_winners' => GiveawayWinners::class, + 'giveaway_completed' => GiveawayCompleted::class, 'video_chat_scheduled' => VideoChatScheduled::class, 'video_chat_started' => VideoChatStarted::class, 'video_chat_ended' => VideoChatEnded::class, @@ -286,7 +302,7 @@ public function getType(): string 'pinned_message', 'invoice', 'successful_payment', - 'user_shared', + 'users_shared', 'chat_shared', 'write_access_allowed', 'passport_data', diff --git a/src/Entities/Message/Factory.php b/src/Entities/Message/Factory.php new file mode 100644 index 00000000..63ce362e --- /dev/null +++ b/src/Entities/Message/Factory.php @@ -0,0 +1,21 @@ + Chat::class, + ]; + } +} diff --git a/src/Entities/Message/MaybeInaccessibleMessage.php b/src/Entities/Message/MaybeInaccessibleMessage.php new file mode 100644 index 00000000..8e9d1b85 --- /dev/null +++ b/src/Entities/Message/MaybeInaccessibleMessage.php @@ -0,0 +1,15 @@ + MessageOriginUser::class, + 'hidden_user' => MessageOriginHiddenUser::class, + 'chat' => MessageOriginChat::class, + 'channel' => MessageOriginChannel::class, + ]; + + if (!isset($type[$data['type'] ?? ''])) { + return new MessageOriginNotImplemented($data, $bot_username); + } + + $class = $type[$data['type']]; + return new $class($data, $bot_username); + } +} diff --git a/src/Entities/MessageOrigin/MessageOrigin.php b/src/Entities/MessageOrigin/MessageOrigin.php new file mode 100644 index 00000000..a8a468c0 --- /dev/null +++ b/src/Entities/MessageOrigin/MessageOrigin.php @@ -0,0 +1,14 @@ + Chat::class, + ]; + } +} diff --git a/src/Entities/MessageOrigin/MessageOriginChat.php b/src/Entities/MessageOrigin/MessageOriginChat.php new file mode 100644 index 00000000..a0c2b398 --- /dev/null +++ b/src/Entities/MessageOrigin/MessageOriginChat.php @@ -0,0 +1,26 @@ + Chat::class, + ]; + } +} diff --git a/src/Entities/MessageOrigin/MessageOriginHiddenUser.php b/src/Entities/MessageOrigin/MessageOriginHiddenUser.php new file mode 100644 index 00000000..baff892f --- /dev/null +++ b/src/Entities/MessageOrigin/MessageOriginHiddenUser.php @@ -0,0 +1,19 @@ + User::class, + ]; + } +} diff --git a/src/Entities/MessageReactionCountUpdated.php b/src/Entities/MessageReactionCountUpdated.php new file mode 100644 index 00000000..21dc848b --- /dev/null +++ b/src/Entities/MessageReactionCountUpdated.php @@ -0,0 +1,24 @@ + Chat::class, + 'reactions' => [ReactionCount::class], + ]; + } +} diff --git a/src/Entities/MessageReactionUpdated.php b/src/Entities/MessageReactionUpdated.php new file mode 100644 index 00000000..c5b60214 --- /dev/null +++ b/src/Entities/MessageReactionUpdated.php @@ -0,0 +1,33 @@ + Chat::class, + 'user' => User::class, + 'actor_chat' => Chat::class, + 'old_reaction' => [ReactionTypeFactory::class], + 'new_reaction' => [ReactionTypeFactory::class], + ]; + } +} diff --git a/src/Entities/ReactionCount.php b/src/Entities/ReactionCount.php new file mode 100644 index 00000000..c99e67d3 --- /dev/null +++ b/src/Entities/ReactionCount.php @@ -0,0 +1,24 @@ + ReactionTypeFactory::class, + ]; + } +} diff --git a/src/Entities/ReactionType/Factory.php b/src/Entities/ReactionType/Factory.php new file mode 100644 index 00000000..f61cf368 --- /dev/null +++ b/src/Entities/ReactionType/Factory.php @@ -0,0 +1,23 @@ + ReactionTypeEmoji::class, + 'custom_emoji' => ReactionTypeCustomEmoji::class, + ]; + + if (!isset($type[$data['type'] ?? ''])) { + return new ReactionTypeNotImplemented($data, $bot_username); + } + + $class = $type[$data['type']]; + return new $class($data, $bot_username); + } +} diff --git a/src/Entities/ReactionType/ReactionType.php b/src/Entities/ReactionType/ReactionType.php new file mode 100644 index 00000000..901f951d --- /dev/null +++ b/src/Entities/ReactionType/ReactionType.php @@ -0,0 +1,15 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +/** + * Describes reply parameters for the message that is being sent. + * + * @link https://core.telegram.org/bots/api#replyparameters + * + * @method int getMessageId() Identifier of the message that will be replied to in the current chat, or in the chat chat_id if it is specified + * @method int|string getChatId() Optional. If the message to be replied to is from a different chat, unique identifier for the chat or username of the channel (in the format @channelusername) + * @method bool getAllowSendingWithoutReply() Optional. Pass True if the message should be sent even if the specified message to be replied to is not found; can be used only for replies in the same chat and forum topic. + * @method string getQuote() Optional. Quoted part of the message to be replied to; 0-1024 characters after entities parsing. The quote must be an exact substring of the message to be replied to, including bold, italic, underline, strikethrough, spoiler, and custom_emoji entities. The message will fail to send if the quote isn't found in the original message. + * @method string getQuoteParseMode() Optional. Mode for parsing entities in the quote. See formatting options for more details. + * @method MessageEntity[] getQuoteEntities() Optional. A JSON-serialized list of special entities that appear in the quote. It can be specified instead of quote_parse_mode. + * @method int getQuotePosition() Optional. Position of the quote in the original message in UTF-16 code units + */ +class ReplyParameters extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'quote_entities' => [MessageEntity::class], + ]; + } +} diff --git a/src/Entities/TextQuote.php b/src/Entities/TextQuote.php new file mode 100644 index 00000000..9e237d79 --- /dev/null +++ b/src/Entities/TextQuote.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +/** + * This object contains information about the quoted part of a message that is replied to by the given message. + * + * @link https://core.telegram.org/bots/api#textquote + * + * @method string getText() Text of the quoted part of a message that is replied to by the given message + * @method MessageEntity[] getEntities() Optional. Special entities that appear in the quote. Currently, only bold, italic, underline, strikethrough, spoiler, and custom_emoji entities are kept in quotes. + * @method int getPosition() Approximate quote position in the original message in UTF-16 code units as specified by the sender + * @method bool getIsManual() Optional. True, if the quote was chosen manually by the message sender. Otherwise, the quote was added automatically by the server. + */ +class TextQuote extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'entities' => [MessageEntity::class], + ]; + } +} diff --git a/src/Entities/Update.php b/src/Entities/Update.php index 3e56602a..716b1830 100644 --- a/src/Entities/Update.php +++ b/src/Entities/Update.php @@ -19,38 +19,46 @@ * * @link https://core.telegram.org/bots/api#update * - * @method int getUpdateId() The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you’re using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. - * @method Message getMessage() Optional. New incoming message of any kind — text, photo, sticker, etc. - * @method Message getEditedMessage() Optional. New version of a message that is known to the bot and was edited - * @method Message getChannelPost() Optional. New post in the channel, can be any kind — text, photo, sticker, etc. - * @method Message getEditedChannelPost() Optional. New version of a post in the channel that is known to the bot and was edited - * @method InlineQuery getInlineQuery() Optional. New incoming inline query - * @method ChosenInlineResult getChosenInlineResult() Optional. The result of an inline query that was chosen by a user and sent to their chat partner. - * @method CallbackQuery getCallbackQuery() Optional. New incoming callback query - * @method ShippingQuery getShippingQuery() Optional. New incoming shipping query. Only for invoices with flexible price - * @method PreCheckoutQuery getPreCheckoutQuery() Optional. New incoming pre-checkout query. Contains full information about checkout - * @method Poll getPoll() Optional. New poll state. Bots receive only updates about polls, which are sent or stopped by the bot - * @method PollAnswer getPollAnswer() Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself. - * @method ChatMemberUpdated getMyChatMember() Optional. The bot's chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user. - * @method ChatMemberUpdated getChatMember() Optional. A chat member's status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. - * @method ChatJoinRequest getChatJoinRequest() Optional. A request to join the chat has been sent. The bot must have the can_invite_users administrator right in the chat to receive these updates. + * @method int getUpdateId() The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you’re using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. + * @method Message getMessage() Optional. New incoming message of any kind — text, photo, sticker, etc. + * @method Message getEditedMessage() Optional. New version of a message that is known to the bot and was edited + * @method Message getChannelPost() Optional. New post in the channel, can be any kind — text, photo, sticker, etc. + * @method Message getEditedChannelPost() Optional. New version of a post in the channel that is known to the bot and was edited + * @method MessageReactionUpdated getMessageReaction() Optional. A reaction to a message was changed by a user. The bot must be an administrator in the chat and must explicitly specify "message_reaction" in the list of allowed_updates to receive these updates. The update isn't received for reactions set by bots. + * @method MessageReactionCountUpdated getMessageReactionCount() Optional. Reactions to a message with anonymous reactions were changed. The bot must be an administrator in the chat and must explicitly specify "message_reaction_count" in the list of allowed_updates to receive these updates. The updates are grouped and can be sent with delay up to a few minutes. + * @method InlineQuery getInlineQuery() Optional. New incoming inline query + * @method ChosenInlineResult getChosenInlineResult() Optional. The result of an inline query that was chosen by a user and sent to their chat partner. + * @method CallbackQuery getCallbackQuery() Optional. New incoming callback query + * @method ShippingQuery getShippingQuery() Optional. New incoming shipping query. Only for invoices with flexible price + * @method PreCheckoutQuery getPreCheckoutQuery() Optional. New incoming pre-checkout query. Contains full information about checkout + * @method Poll getPoll() Optional. New poll state. Bots receive only updates about polls, which are sent or stopped by the bot + * @method PollAnswer getPollAnswer() Optional. A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself. + * @method ChatMemberUpdated getMyChatMember() Optional. The bot's chat member status was updated in a chat. For private chats, this update is received only when the bot is blocked or unblocked by the user. + * @method ChatMemberUpdated getChatMember() Optional. A chat member's status was updated in a chat. The bot must be an administrator in the chat and must explicitly specify “chat_member” in the list of allowed_updates to receive these updates. + * @method ChatJoinRequest getChatJoinRequest() Optional. A request to join the chat has been sent. The bot must have the can_invite_users administrator right in the chat to receive these updates. + * @method ChatBoostUpdated getChatBoost() Optional. A chat boost was added or changed. The bot must be an administrator in the chat to receive these updates. + * @method ChatBoostRemoved getRemovedChatBoost() Optional. A boost was removed from a chat. The bot must be an administrator in the chat to receive these updates. */ class Update extends Entity { - public const TYPE_MESSAGE = 'message'; - public const TYPE_EDITED_MESSAGE = 'edited_message'; - public const TYPE_CHANNEL_POST = 'channel_post'; - public const TYPE_EDITED_CHANNEL_POST = 'edited_channel_post'; - public const TYPE_INLINE_QUERY = 'inline_query'; - public const TYPE_CHOSEN_INLINE_RESULT = 'chosen_inline_result'; - public const TYPE_CALLBACK_QUERY = 'callback_query'; - public const TYPE_SHIPPING_QUERY = 'shipping_query'; - public const TYPE_PRE_CHECKOUT_QUERY = 'pre_checkout_query'; - public const TYPE_POLL = 'poll'; - public const TYPE_POLL_ANSWER = 'poll_answer'; - public const TYPE_MY_CHAT_MEMBER = 'my_chat_member'; - public const TYPE_CHAT_MEMBER = 'chat_member'; - public const TYPE_CHAT_JOIN_REQUEST = 'chat_join_request'; + public const TYPE_MESSAGE = 'message'; + public const TYPE_EDITED_MESSAGE = 'edited_message'; + public const TYPE_CHANNEL_POST = 'channel_post'; + public const TYPE_EDITED_CHANNEL_POST = 'edited_channel_post'; + public const TYPE_MESSAGE_REACTION = 'message_reaction'; + public const TYPE_MESSAGE_REACTION_COUNT = 'message_reaction_count'; + public const TYPE_INLINE_QUERY = 'inline_query'; + public const TYPE_CHOSEN_INLINE_RESULT = 'chosen_inline_result'; + public const TYPE_CALLBACK_QUERY = 'callback_query'; + public const TYPE_SHIPPING_QUERY = 'shipping_query'; + public const TYPE_PRE_CHECKOUT_QUERY = 'pre_checkout_query'; + public const TYPE_POLL = 'poll'; + public const TYPE_POLL_ANSWER = 'poll_answer'; + public const TYPE_MY_CHAT_MEMBER = 'my_chat_member'; + public const TYPE_CHAT_MEMBER = 'chat_member'; + public const TYPE_CHAT_JOIN_REQUEST = 'chat_join_request'; + public const TYPE_CHAT_BOOST = 'chat_boost'; + public const TYPE_REMOVED_CHAT_BOOST = 'removed_chat_boost'; /** * {@inheritdoc} @@ -58,20 +66,24 @@ class Update extends Entity protected function subEntities(): array { return [ - self::TYPE_MESSAGE => Message::class, - self::TYPE_EDITED_MESSAGE => EditedMessage::class, - self::TYPE_CHANNEL_POST => ChannelPost::class, - self::TYPE_EDITED_CHANNEL_POST => EditedChannelPost::class, - self::TYPE_INLINE_QUERY => InlineQuery::class, - self::TYPE_CHOSEN_INLINE_RESULT => ChosenInlineResult::class, - self::TYPE_CALLBACK_QUERY => CallbackQuery::class, - self::TYPE_SHIPPING_QUERY => ShippingQuery::class, - self::TYPE_PRE_CHECKOUT_QUERY => PreCheckoutQuery::class, - self::TYPE_POLL => Poll::class, - self::TYPE_POLL_ANSWER => PollAnswer::class, - self::TYPE_MY_CHAT_MEMBER => ChatMemberUpdated::class, - self::TYPE_CHAT_MEMBER => ChatMemberUpdated::class, - self::TYPE_CHAT_JOIN_REQUEST => ChatJoinRequest::class, + self::TYPE_MESSAGE => Message::class, + self::TYPE_EDITED_MESSAGE => EditedMessage::class, + self::TYPE_CHANNEL_POST => ChannelPost::class, + self::TYPE_EDITED_CHANNEL_POST => EditedChannelPost::class, + self::TYPE_MESSAGE_REACTION => MessageReactionUpdated::class, + self::TYPE_MESSAGE_REACTION_COUNT => MessageReactionCountUpdated::class, + self::TYPE_INLINE_QUERY => InlineQuery::class, + self::TYPE_CHOSEN_INLINE_RESULT => ChosenInlineResult::class, + self::TYPE_CALLBACK_QUERY => CallbackQuery::class, + self::TYPE_SHIPPING_QUERY => ShippingQuery::class, + self::TYPE_PRE_CHECKOUT_QUERY => PreCheckoutQuery::class, + self::TYPE_POLL => Poll::class, + self::TYPE_POLL_ANSWER => PollAnswer::class, + self::TYPE_MY_CHAT_MEMBER => ChatMemberUpdated::class, + self::TYPE_CHAT_MEMBER => ChatMemberUpdated::class, + self::TYPE_CHAT_JOIN_REQUEST => ChatJoinRequest::class, + self::TYPE_CHAT_BOOST => ChatBoostUpdated::class, + self::TYPE_REMOVED_CHAT_BOOST => ChatBoostRemoved::class, ]; } @@ -104,7 +116,7 @@ public function getUpdateType(): ?string /** * Get update content * - * @return CallbackQuery|ChatMemberUpdated|ChosenInlineResult|InlineQuery|Message|PollAnswer|Poll|PreCheckoutQuery|ShippingQuery + * @return Message|EditedMessage|ChannelPost|EditedChannelPost|MessageReactionUpdated|MessageReactionCountUpdated|InlineQuery|ChosenInlineResult|CallbackQuery|ShippingQuery|PreCheckoutQuery|Poll|PollAnswer|ChatMemberUpdated|ChatJoinRequest|ChatBoostUpdated|ChatBoostRemoved */ public function getUpdateContent() { diff --git a/src/Entities/UserChatBoosts.php b/src/Entities/UserChatBoosts.php new file mode 100644 index 00000000..de45ddb1 --- /dev/null +++ b/src/Entities/UserChatBoosts.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Longman\TelegramBot\Entities; + +/** + * This object represents a list of boosts added to a chat by a user. + * + * @link https://core.telegram.org/bots/api#userchatboosts + * + * @method ChatBoost[] getBoosts() The list of boosts added to the chat by the user + */ +class UserChatBoosts extends Entity +{ + /** + * {@inheritdoc} + */ + protected function subEntities(): array + { + return [ + 'boosts' => [ChatBoost::class], + ]; + } +} diff --git a/src/Entities/UserShared.php b/src/Entities/UserShared.php deleted file mode 100644 index aac35d2c..00000000 --- a/src/Entities/UserShared.php +++ /dev/null @@ -1,18 +0,0 @@ -/, where is taken from the response. It is guaranteed that the link will be valid for at least 1 hour. When the link expires, a new one can be requested by calling getFile again. * @method static ServerResponse banChatMember(array $data) Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the group on their own using invite links, etc., unless unbanned first. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success. @@ -97,6 +100,7 @@ * @method static ServerResponse unpinAllGeneralForumTopicMessages(array $data) Use this method to clear the list of pinned messages in a General forum topic. The bot must be an administrator in the chat for this to work and must have the can_pin_messages administrator right in the supergroup. Returns True on success. * @method static ServerResponse answerCallbackQuery(array $data) Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. On success, True is returned. * @method static ServerResponse answerInlineQuery(array $data) Use this method to send answers to an inline query. On success, True is returned. + * @method static ServerResponse getUserChatBoosts(array $data) Use this method to get the list of boosts added to a chat by a user. Requires administrator rights in the chat. Returns a UserChatBoosts object. * @method static ServerResponse setMyCommands(array $data) Use this method to change the list of the bot's commands. Returns True on success. * @method static ServerResponse deleteMyCommands(array $data) Use this method to delete the list of the bot's commands for the given scope and user language. After deletion, higher level commands will be shown to affected users. Returns True on success. * @method static ServerResponse getMyCommands(array $data) Use this method to get the current list of the bot's commands. Requires no parameters. Returns Array of BotCommand on success. @@ -116,6 +120,7 @@ * @method static ServerResponse editMessageReplyMarkup(array $data) Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots). On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. * @method static ServerResponse stopPoll(array $data) Use this method to stop a poll which was sent by the bot. On success, the stopped Poll with the final results is returned. * @method static ServerResponse deleteMessage(array $data) Use this method to delete a message, including service messages, with certain limitations. Returns True on success. + * @method static ServerResponse deleteMessages(array $data) Use this method to delete multiple messages simultaneously. If some of the specified messages can't be found, they are skipped. Returns True on success. * @method static ServerResponse getStickerSet(array $data) Use this method to get a sticker set. On success, a StickerSet object is returned. * @method static ServerResponse getCustomEmojiStickers(array $data) Use this method to get information about custom emoji stickers by their identifiers. Returns an Array of Sticker objects. * @method static ServerResponse uploadStickerFile(array $data) Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). Returns the uploaded File on success. @@ -210,7 +215,9 @@ class Request 'close', 'sendMessage', 'forwardMessage', + 'forwardMessages', 'copyMessage', + 'copyMessages', 'sendPhoto', 'sendAudio', 'sendDocument', @@ -228,6 +235,7 @@ class Request 'sendPoll', 'sendDice', 'sendChatAction', + 'setMessageReaction', 'getUserProfilePhotos', 'getFile', 'banChatMember', @@ -273,6 +281,7 @@ class Request 'unpinAllGeneralForumTopicMessages', 'answerCallbackQuery', 'answerInlineQuery', + 'getUserChatBoosts', 'setMyCommands', 'deleteMyCommands', 'getMyCommands', @@ -292,6 +301,7 @@ class Request 'editMessageReplyMarkup', 'stopPoll', 'deleteMessage', + 'deleteMessages', 'getStickerSet', 'getCustomEmojiStickers', 'uploadStickerFile', @@ -933,7 +943,9 @@ private static function limitTelegramRequests(string $action, array $data = []): $limited_methods = [ 'sendMessage', 'forwardMessage', + 'forwardMessages', 'copyMessage', + 'copyMessages', 'sendPhoto', 'sendAudio', 'sendDocument', @@ -950,6 +962,7 @@ private static function limitTelegramRequests(string $action, array $data = []): 'sendContact', 'sendPoll', 'sendDice', + 'setMessageReaction', 'sendInvoice', 'sendGame', 'setGameScore', @@ -960,6 +973,8 @@ private static function limitTelegramRequests(string $action, array $data = []): 'editMessageMedia', 'editMessageReplyMarkup', 'stopPoll', + 'deleteMessage', + 'deleteMessages', 'setChatTitle', 'setChatDescription', 'setChatStickerSet', diff --git a/structure.sql b/structure.sql index 205dadec..cf806153 100644 --- a/structure.sql +++ b/structure.sql @@ -41,6 +41,39 @@ CREATE TABLE IF NOT EXISTS `user_chat` ( FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; +CREATE TABLE IF NOT EXISTS `message_reaction` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'The chat containing the message the user reacted to', + `message_id` bigint COMMENT 'Unique identifier of the message inside the chat', + `user_id` bigint NULL COMMENT 'Optional. The user that changed the reaction, if the user isn''t anonymous', + `actor_chat_id` bigint NULL COMMENT 'Optional. The chat on behalf of which the reaction was changed, if the user is anonymous', + `old_reaction` TEXT NOT NULL COMMENT 'Previous list of reaction types that were set by the user', + `new_reaction` TEXT NOT NULL COMMENT 'New list of reaction types that have been set by the user', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + KEY `user_id` (`user_id`), + KEY `actor_chat_id` (`actor_chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`), + FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), + FOREIGN KEY (`actor_chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +CREATE TABLE IF NOT EXISTS `message_reaction_count` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'The chat containing the message', + `message_id` bigint COMMENT 'Unique message identifier inside the chat', + `reactions` TEXT NOT NULL COMMENT 'List of reactions that are present on the message', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + CREATE TABLE IF NOT EXISTS `inline_query` ( `id` bigint UNSIGNED COMMENT 'Unique identifier for this query', `user_id` bigint NULL COMMENT 'Unique user identifier', @@ -88,7 +121,9 @@ CREATE TABLE IF NOT EXISTS `message` ( `is_automatic_forward` tinyint(1) DEFAULT 0 COMMENT 'True, if the message is a channel post that was automatically forwarded to the connected discussion group', `reply_to_chat` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier', `reply_to_message` bigint UNSIGNED DEFAULT NULL COMMENT 'Message that this message is reply to', + `external_reply` TEXT NULL DEFAULT NULL COMMENT 'Optional. Information about the message that is being replied to, which may come from another chat or forum topic', `via_bot` bigint NULL DEFAULT NULL COMMENT 'Optional. Bot through which the message was sent', + `link_preview_options` TEXT NULL DEFAULT NULL COMMENT 'Optional. Options used for link preview generation for the message, if it is a text message and link preview options were changed', `edit_date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was last edited in Unix time', `has_protected_content` tinyint(1) DEFAULT 0 COMMENT 'True, if the message can''t be forwarded', `media_group_id` TEXT COMMENT 'The unique identifier of a media message group this message belongs to', @@ -127,7 +162,7 @@ CREATE TABLE IF NOT EXISTS `message` ( `pinned_message` TEXT NULL COMMENT 'Message object. Specified message was pinned', `invoice` TEXT NULL COMMENT 'Message is an invoice for a payment, information about the invoice', `successful_payment` TEXT NULL COMMENT 'Message is a service message about a successful payment, information about the payment', - `user_shared` TEXT NULL COMMENT 'Optional. Service message: a user was shared with the bot', + `users_shared` TEXT NULL COMMENT 'Optional. Service message: users were shared with the bot', `chat_shared` TEXT NULL COMMENT 'Optional. Service message: a chat was shared with the bot', `connected_website` TEXT NULL COMMENT 'The domain name of the website on which the user has logged in.', `write_access_allowed` TEXT DEFAULT NULL COMMENT 'Service message: the user allowed the bot added to the attachment menu to write messages', @@ -294,6 +329,32 @@ CREATE TABLE IF NOT EXISTS `chat_join_request` ( FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; +CREATE TABLE IF NOT EXISTS `chat_boost_updated` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'Chat which was boosted', + `boost` TEXT NOT NULL COMMENT 'Information about the chat boost', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +CREATE TABLE IF NOT EXISTS `chat_boost_removed` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'Chat which was boosted', + `boost_id` varchar(200) NOT NULL COMMENT 'Unique identifier of the boost', + `remove_date` timestamp NOT NULL COMMENT 'Point in time (Unix timestamp) when the boost was removed', + `source` TEXT NOT NULL COMMENT 'Source of the removed boost', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + CREATE TABLE IF NOT EXISTS `telegram_update` ( `id` bigint UNSIGNED COMMENT 'Update''s unique identifier', `chat_id` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier', @@ -301,6 +362,8 @@ CREATE TABLE IF NOT EXISTS `telegram_update` ( `edited_message_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New version of a message that is known to the bot and was edited', `channel_post_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming channel post of any kind - text, photo, sticker, etc.', `edited_channel_post_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New version of a channel post that is known to the bot and was edited', + `message_reaction_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A reaction to a message was changed by a user', + `message_reaction_count_id` bigint UNSIGNED DEFAULT NULL COMMENT 'Reactions to a message with anonymous reactions were changed', `inline_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming inline query', `chosen_inline_result_id` bigint UNSIGNED DEFAULT NULL COMMENT 'The result of an inline query that was chosen by a user and sent to their chat partner', `callback_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming callback query', diff --git a/tests/Unit/Entities/KeyboardButtonTest.php b/tests/Unit/Entities/KeyboardButtonTest.php index d8fc7840..e7cd907b 100644 --- a/tests/Unit/Entities/KeyboardButtonTest.php +++ b/tests/Unit/Entities/KeyboardButtonTest.php @@ -14,7 +14,7 @@ use Longman\TelegramBot\Entities\KeyboardButton; use Longman\TelegramBot\Entities\KeyboardButtonPollType; use Longman\TelegramBot\Entities\KeyboardButtonRequestChat; -use Longman\TelegramBot\Entities\KeyboardButtonRequestUser; +use Longman\TelegramBot\Entities\KeyboardButtonRequestUsers; use Longman\TelegramBot\Entities\WebAppInfo; use Longman\TelegramBot\Exception\TelegramException; use Longman\TelegramBot\Tests\Unit\TestCase; @@ -31,7 +31,7 @@ class KeyboardButtonTest extends TestCase public function testKeyboardButtonSuccess(): void { new KeyboardButton(['text' => 'message']); - new KeyboardButton(['text' => 'message', 'request_user' => new KeyboardButtonRequestUser([])]); + new KeyboardButton(['text' => 'message', 'request_users' => new KeyboardButtonRequestUsers([])]); new KeyboardButton(['text' => 'message', 'request_chat' => new KeyboardButtonRequestChat([])]); new KeyboardButton(['text' => 'message', 'request_contact' => true]); new KeyboardButton(['text' => 'message', 'request_location' => true]); @@ -49,8 +49,8 @@ public function testInlineKeyboardButtonCouldBe(): void public function testReturnsSubentitiesOnArray() { $button = new KeyboardButton('message'); - $button->request_user = []; - $this->assertInstanceOf(KeyboardButtonRequestUser::class, $button->getRequestUser()); + $button->request_users = []; + $this->assertInstanceOf(KeyboardButtonRequestUsers::class, $button->getRequestUsers()); $button = new KeyboardButton('message'); $button->request_chat = []; diff --git a/utils/db-schema-update/0.82.0-unreleased.sql b/utils/db-schema-update/0.82.0-unreleased.sql new file mode 100644 index 00000000..3fb04f37 --- /dev/null +++ b/utils/db-schema-update/0.82.0-unreleased.sql @@ -0,0 +1,67 @@ +CREATE TABLE IF NOT EXISTS `message_reaction` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'The chat containing the message the user reacted to', + `message_id` bigint COMMENT 'Unique identifier of the message inside the chat', + `user_id` bigint NULL COMMENT 'Optional. The user that changed the reaction, if the user isn''t anonymous', + `actor_chat_id` bigint NULL COMMENT 'Optional. The chat on behalf of which the reaction was changed, if the user is anonymous', + `old_reaction` TEXT NOT NULL COMMENT 'Previous list of reaction types that were set by the user', + `new_reaction` TEXT NOT NULL COMMENT 'New list of reaction types that have been set by the user', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + KEY `user_id` (`user_id`), + KEY `actor_chat_id` (`actor_chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`), + FOREIGN KEY (`user_id`) REFERENCES `user` (`id`), + FOREIGN KEY (`actor_chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +CREATE TABLE IF NOT EXISTS `message_reaction_count` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'The chat containing the message', + `message_id` bigint COMMENT 'Unique message identifier inside the chat', + `reactions` TEXT NOT NULL COMMENT 'List of reactions that are present on the message', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +CREATE TABLE IF NOT EXISTS `chat_boost_updated` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'Chat which was boosted', + `boost` TEXT NOT NULL COMMENT 'Information about the chat boost', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +CREATE TABLE IF NOT EXISTS `chat_boost_removed` ( + `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry', + `chat_id` bigint COMMENT 'Chat which was boosted', + `boost_id` varchar(200) NOT NULL COMMENT 'Unique identifier of the boost', + `remove_date` timestamp NOT NULL COMMENT 'Point in time (Unix timestamp) when the boost was removed', + `source` TEXT NOT NULL COMMENT 'Source of the removed boost', + `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation', + + PRIMARY KEY (`id`), + KEY `chat_id` (`chat_id`), + + FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci; + +ALTER TABLE `message` + ADD COLUMN `external_reply` TEXT NULL DEFAULT NULL COMMENT 'Optional. Information about the message that is being replied to, which may come from another chat or forum topic' AFTER `reply_to_message`, + ADD COLUMN `link_preview_options` TEXT NULL DEFAULT NULL COMMENT 'Optional. Options used for link preview generation for the message, if it is a text message and link preview options were changed' AFTER `via_bot`, + CHANGE COLUMN `user_shared` `users_shared` TEXT; + +ALTER TABLE `telegram_update` + ADD COLUMN `message_reaction_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A reaction to a message was changed by a user' AFTER `edited_channel_post_id`, + ADD COLUMN `message_reaction_count_id` bigint UNSIGNED DEFAULT NULL COMMENT 'Reactions to a message with anonymous reactions were changed' AFTER `message_reaction_id`;