diff --git a/.editorconfig b/.editorconfig
deleted file mode 100644
index d84f8aa61..000000000
--- a/.editorconfig
+++ /dev/null
@@ -1,12 +0,0 @@
-root = true
-
-[*]
-end_of_line = lf
-insert_final_newline = true
-charset = utf-8
-indent_style = space
-indent_size = 4
-trim_trailing_whitespace = true
-
-[*.{yml, yaml}]
-indent_size = 2
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 74350b724..000000000
--- a/.gitattributes
+++ /dev/null
@@ -1,5 +0,0 @@
-/.* export-ignore
-/composer.lock export-ignore
-/phpcs.xml.dist export-ignore
-/phpunit.xml.dist export-ignore
-/tests export-ignore
diff --git a/.github/ISSUE_TEMPLATE/BC_Break.md b/.github/ISSUE_TEMPLATE/BC_Break.md
deleted file mode 100644
index 01457d033..000000000
--- a/.github/ISSUE_TEMPLATE/BC_Break.md
+++ /dev/null
@@ -1,56 +0,0 @@
----
-name: 💥 BC Break
-about: Have you encountered an issue during an upgrade? 💣
-labels: bc break
----
-
-<!--
-Before reporting a BC break, please consult the changelog and wiki entry for the particular version to make sure it's not an expected change:
-- https://github.com/php-telegram-bot/core/blob/master/CHANGELOG.md
-- https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility
--->
-
-### 💥 BC Break Report
-
-<--
-❗ NEVER put your Telegram API key or any other private details here. (like passwords, user IDs, etc.)
-Substitute them like <API_KEY> or <USER_ID> etc.
--->
-
-#### Required Information
-
-<!-- Fill in the relevant information below to help triage your pull request. -->
-<!-- Please enter exact version numbers, not just "latest" etc.! -->
-
-|  ?                        |  !
-| ---                       | ---
-| Operating system          | Name and version
-| PHP Telegram Bot version  | x.y.z
-| PHP version               | x.y.z
-| MySQL version             | x.y.z / none
-| Update Method             | Webhook / getUpdates
-| Self-signed certificate   | yes / no
-| RAW update (if available) | `{...}`
-
-#### Summary
-
-<!-- Provide a summary describing the problem you are experiencing. -->
-
-#### Previous behaviour
-
-<!-- What was the previous (working) behaviour? -->
-
-#### Current behaviour
-
-<!-- What is the current (broken) behaviour? -->
-
-#### How to reproduce
-
-<!--
-Provide steps to reproduce the bug.
-If possible, also add any relevant code snippet.
--->
-
-#### Expected behaviour
-
-<!-- What was the expected (correct) behaviour? -->
diff --git a/.github/ISSUE_TEMPLATE/Bug.md b/.github/ISSUE_TEMPLATE/Bug.md
deleted file mode 100644
index d52a98193..000000000
--- a/.github/ISSUE_TEMPLATE/Bug.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-name: 🐞 Bug Report
-about: Something is broken? 🔨
-labels: bug
----
-
-### 🐞 Bug Report
-
-<--
-❗ NEVER put your Telegram API key or any other private details here. (like passwords, user IDs, etc.)
-Substitute them like <API_KEY> or <USER_ID> etc.
--->
-
-#### Required Information
-
-<!-- Fill in the relevant information below to help triage your pull request. -->
-<!-- Please enter exact version numbers, not just "latest" etc.! -->
-
-|  ?                        |  !
-| ---                       | ---
-| Operating system          | Name and version
-| PHP Telegram Bot version  | x.y.z
-| PHP version               | x.y.z
-| MySQL version             | x.y.z / none
-| Update Method             | Webhook / getUpdates
-| Self-signed certificate   | yes / no
-| RAW update (if available) | `{...}`
-
-#### Summary
-
-<!-- Provide a summary describing the problem you are experiencing. -->
-
-#### Current behaviour
-
-<!-- What is the current (buggy) behaviour? -->
-
-#### How to reproduce
-
-<!--
-Provide steps to reproduce the bug.
-If possible, also add any relevant code snippet.
--->
-
-#### Expected behaviour
-
-<!-- What was the expected (correct) behaviour? -->
diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.md b/.github/ISSUE_TEMPLATE/Feature_Request.md
deleted file mode 100644
index ac4c98293..000000000
--- a/.github/ISSUE_TEMPLATE/Feature_Request.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-name: 🎉 Feature Request
-about: You have a neat idea that should be implemented? 🎩
-labels: feature request
----
-
-### 🎉 Feature Request
-
-#### Summary
-
-<!-- Provide a summary of the feature you would like to see implemented. -->
diff --git a/.github/ISSUE_TEMPLATE/Support_Question.md b/.github/ISSUE_TEMPLATE/Support_Question.md
deleted file mode 100644
index fe6f2b562..000000000
--- a/.github/ISSUE_TEMPLATE/Support_Question.md
+++ /dev/null
@@ -1,38 +0,0 @@
----
-name: ❓ Support Question
-about: Have a problem that you can't figure out? 🤔
-labels: question
----
-
-### ❓ Support Question
-
-<!--
-Before asking a question here, please try asking in the support group first.
-https://telegram.me/PHP_Telegram_Bot_Support
-
-Keep in mind that GitHub is primarily an issue tracker.
--->
-
-<--
-❗ NEVER put your Telegram API key or any other private details here. (like passwords, user IDs, etc.)
-Substitute them like <API_KEY> or <USER_ID> etc.
--->
-
-#### Required Information
-
-<!-- Fill in the relevant information below to help triage your pull request. -->
-<!-- Please enter exact version numbers, not just "latest" etc.! -->
-
-|  ?                        |  !
-| ---                       | ---
-| Operating system          | Name and version
-| PHP Telegram Bot version  | x.y.z
-| PHP version               | x.y.z
-| MySQL version             | x.y.z / none
-| Update Method             | Webhook / getUpdates
-| Self-signed certificate   | yes / no
-| RAW update (if available) | `{...}`
-
-#### Summary
-
-<!-- Describe the issue you are facing here. -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
deleted file mode 100644
index d75c05408..000000000
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
-Important:
-If this pull request is not related to any issue and contains a new feature, please create an issue first for discussion.
-https://github.com/php-telegram-bot/core/issues/new?template=Feature_Request.md
-
-Make sure this pull request is pointed towards the "develop" branch and refers to any issue that it's related to!
--->
-
-<!-- Fill in the relevant information below to help triage your pull request. -->
-
-| ?            |  !
-|---           | ---
-| Type         | bug / feature / improvement
-| BC Break     | yes / no
-| Fixed issues | <!-- use #NUM format to reference an issue -->
-
-#### Summary
-
-<!-- Provide a summary of your change. -->
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
deleted file mode 100644
index 87764e110..000000000
--- a/.github/workflows/tests.yaml
+++ /dev/null
@@ -1,84 +0,0 @@
-name: Tests
-
-on:
-  push:
-    branches: [ master, develop ]
-  pull_request:
-    branches: [ master, develop ]
-
-permissions:
-  contents: read
-
-jobs:
-  tests:
-    name: PHP ${{ matrix.php }} Test
-    runs-on: ubuntu-latest
-
-    strategy:
-      matrix:
-        php: ['8.1', '8.2', '8.3']
-
-    services:
-      mariadb:
-        image: mariadb:10.3
-        ports:
-          - 3306:3306
-        env:
-          MYSQL_ROOT_PASSWORD: root
-        options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
-
-    steps:
-    - name: Checkout
-      uses: actions/checkout@v3
-
-    - name: Verify MariaDB connection
-      run: |
-        while ! mysqladmin ping -h127.0.0.1 -P3306 --silent; do
-          sleep 1
-        done
-
-    - name: Test migrations
-      run: |
-        git fetch origin 0.44.1 && git checkout FETCH_HEAD -- structure.sql
-        mysql -h127.0.0.1 -P3306 -uroot -proot -e "create database telegrambot_migrations; use telegrambot_migrations; source structure.sql;"
-        git checkout HEAD -- structure.sql
-        mysql -h127.0.0.1 -P3306 -uroot -proot -e "create database telegrambot; use telegrambot; source structure.sql;"
-        for SCHEMA_UPDATE_FILE in $(ls utils/db-schema-update); do mysql -h127.0.0.1 -P3306 -uroot -proot -e "use telegrambot_migrations; source utils/db-schema-update/${SCHEMA_UPDATE_FILE};"; done;
-
-    - name: Install PHP
-      uses: shivammathur/setup-php@v2
-      with:
-        php-version: ${{ matrix.php }}
-        extensions: pdo_mysql
-
-    - name: Cache Composer packages
-      id: composer-cache
-      uses: actions/cache@v3
-      with:
-        path: vendor
-        key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-php-
-
-    - name: Validate composer.json and composer.lock
-      run: composer validate --strict
-
-    - name: Install dependencies
-      run: composer install --prefer-dist --no-progress
-
-    - name: Check PHP code
-      run: composer check-code
-
-    - name: Run test suite
-      run: composer test
-
-#     - 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/.gitignore b/.gitignore
index 426dc5a82..179871170 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,5 @@ phpdoc-*
 ._*
 .Spotlight-V100
 .Trashes
+
+/public
\ No newline at end of file
diff --git a/.scrutinizer.yml b/.scrutinizer.yml
deleted file mode 100644
index d788c4678..000000000
--- a/.scrutinizer.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-build:
-  environment:
-    php: 8.1.0
-  nodes:
-    analysis:
-      tests:
-        override:
-          - php-scrutinizer-run
-
-filter:
-  paths:
-    - src/
-    - utils/
-
-tools:
-  external_code_coverage:
-    timeout: 300
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index c7bc92638..000000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,740 +0,0 @@
-# Changelog
-The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
-
-Exclamation symbols (:exclamation:) note something of importance e.g. breaking changes. Click them to learn more.
-
-## [Unreleased]
-### Notes
-- [: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
-### Security
-
-## [0.82.0] - 2023-10-26
-### Notes
-- [:ledger: View file changes][0.82.0] ∙ [:page_with_curl: DB migration script][0.82.0-sql-migration]
-- Really the last version that supports PHP 7! Only PHP 8 after this one.
-### Added
-- Bot API 6.8, 6.9 (@noplanman) (#1418, #1427)
-- Test against PHP 8.3 (@sergiy-petrov) (#1421)
-### Fixed
-- Missing `thumbnail_url` renaming.
-- Fix broken exception handling (@mlocati) (#1425)
-
-## [0.81.0] - 2023-05-27
-### Notes
-- [:ledger: View file changes][0.81.0] ∙ [:page_with_curl: DB migration script][0.81.0-sql-migration]
-- Last version that supports PHP 7.
-### Added
-- Support dynamic Entity properties for newer PHP versions (@alesinicio, @TiiFuchs) (#1390)
-- Bot API 6.4, 6.5, 6.6, 6.7 (@noplanman) (#1389)
-### Removed
-- Keyboard validations (@noplanman) (#1395)
-### Fixed
-- Fixed a bug where new incoming updates are not correctly passed to the Command object after the first time when getUpdates is used. (@uspilot) (#1384)
-
-## [0.80.0] - 2022-11-09
-### Notes
-- [:ledger: View file changes][0.80.0] ∙ [:page_with_curl: DB migration script][0.80.0-sql-migration]
-### Added
-- Bot API 6.3 (@TiiFuchs) (#1371)
-### Changed
-- [:exclamation:][0.80.0-bc-commands-with-underscores] Commands can now contain underscores (`FooBarCommand` is handled by `/foo_bar`). (@mlocati) (#1365)
-
-## [0.79.0] - 2022-09-04
-### Notes
-- [:ledger: View file changes][0.79.0]
-### Added
-- Bot API 6.2 (@OxMohsen) (#1350)
-### Removed
-- `VoiceChatX` entities, use `VideoChatX` entities instead.
-- `all_members_are_administrators` property from `Message` entity. Database column still exists, but may be removed in the future.
-- `Request::getChatMembersCount()`, use `Request::getChatMemberCount()` instead.
-- `Request::kickChatMember()`, use `Request::banChatMember()` instead.
-
-## [0.78.0] - 2022-07-03
-### Notes
-- [:ledger: View file changes][0.78.0] ∙ [:page_with_curl: DB migration script][0.78.0-sql-migration]
-- 64 bit PHP install required to handle files larger than 2GB!
-### Added
-- Bot API 6.1 (@jyxjjj, @OxMohsen, @noplanman) (#1333, #1338, #1339)
-- New Update field helpers for Command class.
-
-## [0.77.1] - 2022-04-24
-### Notes
-- [:ledger: View file changes][0.77.1]
-### Fixed
-- PHP8+ `mixed` type hint removed
-
-## [0.77.0] - 2022-04-24
-### Notes
-- [:ledger: View file changes][0.77.0] ∙ [:page_with_curl: DB migration script][0.77.0-sql-migration]
-### Added
-- Bot API 6.0 (@TiiFuchs) (#1318)
-### Deprecated
-- Telegram changed `voice_chat_X` to `video_chat_X`. `VoiceChatX` event classes are deprecated, use new `VideoChatX` event classes instead.
-### Fixed
-- Return correct data in `Entity::jsonSerialize` (@TiiFuchs)
-
-## [0.76.1] - 2022-03-30
-### Notes
-- [:ledger: View file changes][0.76.1]
-### Fixed
-- Fix Entity serialising for Keyboards (@TiiFuchs) (#1304)
-
-## [0.76.0] - 2022-03-20
-### Notes
-- [:ledger: View file changes][0.76.0]
-### Added
-- Bot API 5.6 (@TiiFuchs) (#1275)
-- Bot API 5.7 (@TiiFuchs) (#1284)
-- PSR3 (psr/log) version 2 and 3 compatible (@noplanman) (#1287)
-- Entity implements and uses JsonSerializable now (@TiiFuchs)
-- Test with PHP 8.1 on Travis CI (@osavchenko) (#1291)
-### Changed
-- Bugfix: Fixed condition in \Longman\TelegramBot\Entities\Chat::isGroupChat() that previously also counted super groups and channels. (@TiiFuchs)
-
-## [0.75.0] - 2021-12-29
-### Notes
-- [:ledger: View file changes][0.75.0] ∙ [:page_with_curl: DB migration script][0.75.0-sql-migration]
-### Added
-- Ability to directly set commands paths. (@wright-tw, @noplanman) (#1252)
-- Bot API 5.4. (@TiiFuchs, @noplanman) (#1266)
-- Bot API 5.5. (@TiiFuchs, @noplanman) (#1267)
-- The field `message_auto_delete_time` was added to the Chat Entity (@TiiFuchs) (#1265)
-### Removed
-- [:exclamation:][0.75.0-bc-removed-chatactions] Removed deprecated `ChatAction::` `RECORD_AUDIO` and `UPLOAD_AUDIO`. Use `RECORD_VOICE` and `UPLOAD_VOICE` instead. (@TiiFuchs) (#1267)
-### Fixed
-- PHP 8.1 deprecations. (@maxgorovenko) (#1260)
-
-## [0.74.0] - 2021-06-26
-### Notes
-- [:ledger: View file changes][0.74.0]
-### Added
-- [:exclamation:][0.74.0-bc-chatmember-subentities] Bot API 5.3 (Personalized Commands, Keyboard Placeholders). (@TiiFuchs, @noplanman) (#1229, #1231)
-
-## [0.73.1] - 2021-06-20
-### Notes
-- [:ledger: View file changes][0.73.1]
-### Fixed
-- Allow new optional parameters when setting and deleting webhook. (@TiiFuchs) (#1226)
-
-## [0.73.0] - 2021-06-14
-### Notes
-- [:ledger: View file changes][0.73.0] ∙ [:page_with_curl: DB migration script][0.73.0-sql-migration]
-### Added
-- Bot API 5.2 (Payments 2.0). (#1216)
-- Possibility to connect to MySQL DB with unix socket. (@Tynik) (#1220)
-### Changed
-- `Telegram::runCommands` returns array of `ServerResponse` objects of executed commands. (#1223)
-### Fixed
-- Regex for namespace extraction from custom command classes.
-- Nested and user-triggered `Telegram::runCommands`. (#1223)
-
-## [0.72.0] - 2021-04-16
-### Notes
-- [:ledger: View file changes][0.72.0] ∙ [:page_with_curl: DB migration script][0.72.0-sql-migration]
-### Added
-- Bot API 5.1 (ChatMember Update types, Improved Invite Links, Voice Chat). (@massadm, @noplanman) (#1199)
-- Method to allow adding command classes directly. (@alligator77, @noplanman) (#1207, #1209)
-### Deprecated
-- `Telegram::handleGetUpdates` method should be passed a `$data` array for parameters. (#1202)
-### Fixed
-- `message.edit_date` is now of type `timestamp`. (#1191)
-- Allow all update types by default when using `getUpdates` method. (#1202)
-
-## [0.71.0] - 2021-03-05
-### Notes
-- [:ledger: View file changes][0.71.0]
-### Added
-- Define a custom Bot API server and file download URI. (#1168)
-### Changed
-- Improved error messages for empty input. (#1164)
-- Log update when processing it, not when fetching input. (#1164)
-### Fixed
-- `getUpdates` method wrongly sends only 1 Update when a limit of 0 is passed. (#1169)
-- `Telegram::runCommands()` now passes the correct message text to the commands. (#1181)
-- Request limiter accepts chat ID as integer and string. (#1182)
-- Calling Keyboard constructor without any parameters. (@hutattedonmyarm) (#1184)
-
-## [0.70.1] - 2020-12-25
-### Notes
-- [:ledger: View file changes][0.70.1]
-### Added
-- Extra parameter for `Request::sendMessage()` to pass options and return all response objects for split messages. (#1163)
-### Fixed
-- Ensure download and upload path variables are defined. (#1162)
-
-## [0.70.0] - 2020-12-21
-### Notes
-- [:ledger: View file changes][0.70.0] ∙ [:page_with_curl: DB migration script][0.70.0-sql-migration]
-- [:exclamation:][0.70.0-bc-minimum-php-73] PHP 7.3+ required, so make sure your code is up to date with correct types!
-### Added
-- Bot API 5.0 (Big update!). (#1147)
-### Changed
-- Upgrade code to PHP 7.3. (#1136, #1158)
-- Speed up `/clean` query. (@dva-re) (#1139)
-- Various code prettifications. (@akalongman) (#1140, #1141, #1142, #1143)
-### Security
-- Minimum PHP 7.3, allow PHP 8.0. (#1136, #1158)
-
-## [0.64.0] - 2020-10-04
-### Notes
-- [:ledger: View file changes][0.64.0]
-### Added
-- Support for Guzzle 7. (@KristobalJunta) (#1133)
-### Fixed
-- Correct SQL migration script for older versions of MySQL. (#1135)
-
-## [0.63.1] - 2020-06-24
-### Notes
-- [:ledger: View file changes][0.63.1]
-- This fixed version is necessary for Windows users.
-### Fixed
-- Regex in `getFileNamespace()` that introduced a breaking bug in #1114. (@jacklul) (#1115)
-- Fixed `runCommands()` not working due to custom namespace feature. (@jacklul) (#1115, #1118)
-
-## [0.63.0] - 2020-06-17
-### Notes
-- [:ledger: View file changes][0.63.0] ∙ [:page_with_curl: DB migration script][0.63.0-sql-migration]
-### Added
-- New method `setUpdateFilter($callback)` used to filter `processUpdate(Update $update)` calls. If `$callback` returns `false` the update isn't processed and an empty falsey `ServerResponse` is returned. (@VRciF) (#1045)
-- Replaced 'generic' and 'genericmessage' strings with Telegram::GENERIC_COMMAND and Telegram::GENERIC_MESSAGE_COMMAND constants. (@1int) (#1074)
-- Bot API 4.8 (Extra Poll and Dice features). (#1082)
-- Allow custom MySQL port to be defined for tests. (#1090)
-- New static method `Entity::escapeMarkdownV2` for MarkdownV2. (#1094)
-- Remove bot token from debug http logs, this can be disabled by setting `TelegramLog::$remove_bot_token` parameter to `false`. (@jacklul) (#1095)
-- `TelegramLog::$always_log_request_and_response` parameter to force output of the request and response data to the debug log, also for successful requests. (#1089)
-- Bot API 4.9 (New `via_bot` field). (#1112)
-### Changed
-- [:exclamation:][0.63.0-bc-static-method-entityescapemarkdown] Made `Entity::escapeMarkdown` static, to not require an `Entity` object. (#1094)
-- Allow custom namespacing for commands. (@Jonybang) (#689)
-### Fixed
-- Primary key for `poll_answer` also requires the `user_id`. (#1087)
-- Small SQL foreign key fixes. (#1105)
-
-## [0.62.0] - 2020-04-08
-### Notes
-- [:ledger: View file changes][0.62.0] ∙ [:page_with_curl: DB migration script][0.62.0-sql-migration]
-### Added
-- Bot API 4.5 (Unique file IDs, MarkdownV2). (#1046)
-- Chain the exception thrown when getting commands from path. (#1030)
-- Support `language_code` in `DB::selectChats()` for filtering the chats selection. (#1058)
-- Bot API 4.6 (Polls 2.0). (#1066)
-- Bot API 4.7 (Dice, Sticker Sets, Bot Commands). (#1067)
-### Changed
-- Save notes an unescaped JSON, to allow easy DB reading and editing of values. (#1005)
-- `Request::setClient()` now accepts more flexible `ClientInterface`. (#1068)
-### Removed
-- Unnecessary `instanceof` checks for entities. (#1068)
-- Unused `Request::$input` variable. (#1068)
-### Fixed
-- Execution of `/start` command without any custom implementation.
-- Return `animation` type for GIF Message (which returns both `animation` and `document`). (#1044)
-- Change lowercase function to `mb_strtolower` from `strtolower` because of `latin-extented` characters. (#1051)
-- Extend `Request::mediaInputHelper()` to include `thumb` parameter. (#1059)
-- Various docblock annotations. (#1068)
-
-## [0.61.1] - 2019-11-23
-### Notes
-- [:ledger: View file changes][0.61.1]
-### Added
-- Tests for schema update scripts, to ensure that updates work as expected. (#1025)
-### Fixed
-- Parameter `inline_query_id` in `InlineQuery::answerInlineQuery`. (#1021)
-- Corrected DB schema update 0.60.0-0.61.0. (#1025)
-
-## [0.61.0] - 2019-11-02
-### Notes
-- [:ledger: View file changes][0.61.0] ∙ [:page_with_curl: DB migration script][0.61.0-sql-migration]
-- :exclamation: Built-in logging (Monolog) has been removed, a custom PSR-3 logger must be used now! (see #964 for more info)
-### Added
-- Code snippet in `GenericmessageCommand` to keep obsolete service message system commands working. (#999)
-- Static boolean property `SystemCommand::$execute_deprecated` (must be assigned before handling the request) to try and execute any deprecated system command. (#999)
-- Improved MySQL DB index for `message` table, making the cleanup much faster on bigger databases. (Thanks to @damianperez) (#1015)
-- `/cleanup` command now supports dry run which simply outputs all queries that would be run. (#1015)
-### Changed
-- Small readme and code fixes / simplifications. (#1001)
-- Upgrade PHPUnit to 8.x and PHPCS to 3.5. For tests now minimum PHP version is 7.2. (#1008)
-- Updated updates log importer (requires PHP7+). (#1009)
-### Removed
-- Service message system commands, which are now handled by `GenericmessageCommand`. (#999)
-- [:exclamation:][0.61.0-bc-remove-monolog-from-core] Monolog has been removed as built-in logging engine. (#1009)
-- Assets have been moved to a dedicated repository. (#1012)
-### Fixed
-- Boolean value for Polls gets saved correctly in MySQL DB. (#996)
-- Correctly use `Request::answerInlineQuery` in `InlineQuery::answer`. (#1001)
-- PSR-12 incompatibilities in the codebase. (#1008)
-- Improved and corrected `/cleanup` command. (#1015)
-
-## [0.60.0] - 2019-08-16
-### Notes
-- [:ledger: View file changes][0.60.0]
-### Added
-- Bot API 4.4 (Animated stickers, `ChatPermissions`). (#990)
-### Changed
-- Allow passing native entity objects to requests. (#991)
-### Fixed
-- Non-existing commands now correctly execute `GenericCommand` again. (#993)
-
-## [0.59.1] - 2019-07-18
-### Notes
-- [:ledger: View file changes][0.59.1]
-- :exclamation: Deprecated logging method will be removed in the next version, update to PSR-3 now! (see #964 for more info)
-### Added
-- Issue labels set automatically via templates.
-### Changed
-- Logging only updates or only debug/errors is now possible. (#983)
-### Fixed
-- Don't output deprecation notices if no logging is enabled. (#983)
-- Respect custom HTTP Client initialisation, no matter when it is set. (#986)
-
-## [0.59.0] - 2019-07-07
-### Notes
-- [:ledger: View file changes][0.59.0]
-### Changed
-- Touched up readme including header and badges. (#981)
-- Updated changelog to be more useful for external integrations like [Tidelift] and GitHub releases. (#981)
-### Removed
-- Abstract methods from `InputMedia` interface, as method annotations didn't work for propagation. (#978)
-
-## [0.58.0] - 2019-06-26
-### Notes
-- [:ledger: View file changes][0.58.0] ∙ [:page_with_curl: DB migration script][0.58.0-sql-migration]
-- Logging now uses [PSR-3] `LoggerInterface`. Learn more about how to use it here: #964
-### Added
-- New funding and support details. (#971)
-- Custom issue templates. (#972)
-- Bot API 4.3 (Seamless Telegram Login, `LoginUrl`) (#957)
-- `reply_markup` field to `Message` entity. (#957)
-### Changed
-- Use PSR-12 for code style. (#966)
-- Some general housekeeping. (#972)
-- [:exclamation:][0.58.0-bc-return-value-of-empty-entity-properties] Return an empty array for Entity properties with no items, instead of `null`. (#969)
-- `TelegramLog` now adheres to [PSR-3] `LoggerInterface` and allows custom logger implementations. (#964)
-### Deprecated
-- Old logging that uses Monolog still works but will be removed in the near future. Use `TelegramLog::initialize($logger, $update_logger);` from now on. (#964)
-- [:exclamation:][0.58.0-bc-startcommand-is-now-a-usercommand] `StartCommand` is now a `UserCommand` (not `SystemCommand` any more). (#970)
-### Removed
-- Botan.io integration completely removed. (#968)
-### Fixed
-- `forward_date` is now correctly saved to the DB. (#967)
-- Broken `StickerSet::getStickers()` method. (#969)
-- Smaller code and docblock fixes. (#973)
-### Security
-- Security disclosure managed by [Tidelift]. (#971)
-- Don't allow a user to call system commands directly. (#970)
-
-## [0.57.0] - 2019-06-01
-### Notes
-- [:ledger: View file changes][0.57.0] ∙ [:page_with_curl: DB migration script][0.57.0-sql-migration]
-- :grey_exclamation: This is a big update and involves a bunch of MySQL database updates, so please *review the changelog carefully*.
-
-### Added
-- New logo! :tada: (#954)
-- Bot API 4.2 (Polls). (#948)
-- `getIsMember()` method to `ChatMember` entity. (#948)
-- `getForwardSenderName()` method to `Message` entity. (#948)
-- `forward_sender_name` (and forgotten `forward_signature`) DB fields. (#948)
-- Added missing API fields to Entities and DB. (#885)
-- Created database tables for `shipping_query` and `pre_checkout_query`. (#885)
-### Fixed
-- Missing DB table name specifier in `/cleanup` command. (#947)
-
-## [0.56.0] - 2019-04-15
-### Notes
-- [:ledger: View file changes][0.56.0] ∙ [:page_with_curl: DB migration script][0.56.0-sql-migration]
-- :grey_exclamation: This is a big update, so please *review the changelog carefully*!
-### Added
-- Helper for sending `InputMedia` objects using `Request::sendMediaGroup()` and `Request::editMediaMessage()` methods.
-- Allow passing absolute file path for InputFile fields, instead of `Request::encodeFile($path)`. (#934)
-### Changed
-- All Message field types dynamically search for an existing Command class that can handle them. (#940)
-- Upgrade dependencies. (#945)
-### Deprecated
-- Botan.io service has been discontinued. (#925)
-- Most built-in System Commands will be handled by GenericmessageCommand by default in a future release and will require a custom implementation. (#940)
-### Fixed
-- Constraint errors in `/cleanup` command. (#930)
-- Return correct objects for requests. (#934)
-- PHPCS: static before visibility declaration. (#945)
-
-## [0.55.1] - 2019-01-06
-### Notes
-- [:ledger: View file changes][0.55.1]
-### Added
-- Add missing `Request::editMessageMedia()` and `CallbackQuery::getChatInstance()` methods. (#916)
-### Fixed
-- Return correct message type. (#913)
-
-## [0.55.0] - 2018-12-20
-### Notes
-- [:ledger: View file changes][0.55.0] ∙ [:page_with_curl: DB migration script][0.55.0-sql-migration]
-### Added
-- Bot API 4.0 and 4.1 (Telegram Passport) (#870, #871)
-- Test PHP 7.3 with Travis. (#903)
-### Changed
-- [:exclamation:][0.55.0-bc-move-animation-out-of-games-namespace] Move Animation entity out of Games namespace.
-### Fixed
-- Added missing `video_note` to `Message` types.
-
-## [0.54.1] - 2018-10-23
-### Notes
-- [:ledger: View file changes][0.54.1]
-### Fixed
-- `sendToActiveChats` now works correctly for any valid Request action. (#898)
-
-## [0.54.0] - 2018-07-21
-### Notes
-- [:ledger: View file changes][0.54.0] ∙ [:page_with_curl: DB migration script][0.54.0-sql-migration]
-### Added
-- `ChatAction` class to simplify chat action selection. (#859)
-- Telegram Games platform! (#732)
-- Ability to set custom MySQL port. (#860)
-- New `InvalidBotTokenException` exception. (#855)
-### Changed
-- [:exclamation:][0.54.0-bc-rename-constants] Rename and ensure no redefinition of constants: `BASE_PATH` -> `TB_BASE_PATH`, `BASE_COMMANDS_PATH` -> `TB_BASE_COMMANDS_PATH`. (#813)
-- Improve readability of readme code snippets. (#861)
-### Fixed
-- Response from `getStickerSet`. (#838)
-
-## [0.53.0] - 2018-04-01
-### Notes
-- [:ledger: View file changes][0.53.0] ∙ [:page_with_curl: DB migration script][0.53.0-sql-migration]
-### Added
-- Implemented new changes for Bot API 3.6 (streamable InputMediaVideo, connected website). (#799)
-- `Telegram::getLastUpdateId()` method, returns ID of the last update that was processed. (#767)
-- `Telegram::useGetUpdatesWithoutDatabase()` method, enables `Telegram::handleGetUpdates()` to run without a database. (#767)
-### Changed
-- Updated Travis to use Trusty containers (for HHVM) and add PHP 7.2 to the tests. (#739)
-- Add debug log entry instead of throwing an exception for duplicate updates. (#765)
-- `Telegram::handleGetUpdates()` can now work without a database connection (not enabled by default). (#767)
-- Improved `/sendtochannel` and `/sendtoall` commands, using new message helpers. (#810)
-### Fixed
-- PHPCS fixes for updated CodeSniffer dependency. (#739)
-- Send messages correctly via `/sendtochannel`. (#803)
-
-## [0.52.0] - 2018-01-07
-### Notes
-- [:ledger: View file changes][0.52.0]
-### Fixed
-- Entity relations and wrong types for payments. (#731)
-- Allow empty string for `switch_inline_query` and `switch_inline_query_current_chat` (InlineKeyboardButton). (#736)
-- Fix empty date entry for User and Chat entities, using the current timestamp instead. (#738)
-
-## [0.51.0] - 2017-12-05
-### Notes
-- [:ledger: View file changes][0.51.0] ∙ [:page_with_curl: DB migration script][0.51.0-sql-migration]
-### Added
-- Implemented new changes for Bot API 3.5 (InputMedia, MediaGroup). (#718)
-
-## [0.50.0] - 2017-10-17
-### Notes
-- [:ledger: View file changes][0.50.0]
-### Added
-- Finish implementing payments, adding all missing type checks and docblock methods. (#647)
-- Implemented new changes for Bot API 3.4 (Live Locations). (#675)
-### Changed
-- [:exclamation:][0.50.0-bc-messagegetcommand-return-value] `Message::getCommand()` returns `null` if not a command, instead of `false`. (#654)
-### Fixed
-- SQL update script for version 0.44.1-0.45.0.
-- Issues found by Scrutinizer (Type hints and return values). (#654)
-- Check inline keyboard button parameter value correctly. (#672)
-
-## [0.49.0] - 2017-09-17
-### Notes
-- [:ledger: View file changes][0.49.0]
-### Added
-- Donation section and links in readme.
-- Missing payment methods in `Request` class.
-- Some helper methods for replying to commands and answering queries.
-### Changed
-- Updated and optimised all DB classes, removing a lot of bulky code.
-### Fixed
-- Ensure named SQL statement parameters are unique.
-- Channel selection when using `DB::selectChats()`.
-
-## [0.48.0] - 2017-08-26
-### Notes
-- [:ledger: View file changes][0.48.0] ∙ [:page_with_curl: DB migration script][0.48.0-sql-migration]
-### Added
-- New entities, methods, update types and inline keyboard button for Payments (Bot API 3.0).
-- Add new methods, fields and objects for working with stickers (Bot API 3.2).
-- New fields for Chat, User and Message objects (Bot API 3.3). `is_bot` added to `user` DB table.
-### Changed
-- [:exclamation:][0.48.0-bc-correct-printerror] Corrected `ServerResponse->printError` method to print by default and return by setting `$return` parameter.
-- Ensure command names are handled as lower case.
-### Fixed
-- Correctly save `reply_to_message` to DB.
-
-## [0.47.1] - 2017-08-06
-### Notes
-- [:ledger: View file changes][0.47.1]
-### Added
-- Linked version numbers in changelog for easy verification of code changes.
-### Fixed
-- Private-only commands work with edited messages now too.
-
-## [0.47.0] - 2017-08-06 [YANKED]
-### Notes
-- [:ledger: View file changes][0.47.0]
-### Changed
-- Updated readme to latest state of 0.47.0.
-### Fixed
-- `Telegram::enableAdmin()` now handles duplicate additions properly.
-- `Request::getMe()` failure doesn't break cron execution any more.
-### Security
-- [:exclamation:][0.47.0-bc-private-only-admin-commands] New command parameter `$private_only` to enforce usage in private chats only (set by default for Admin commands).
-
-## [0.46.0] - 2017-07-15
-### Notes
-- [:ledger: View file changes][0.46.0]
-### Added
-- Callbacks can be added to be executed when callback queries are called.
-- New Bot API 3.1 changes (#550).
-- `/cleanup` command for admins, that cleans out old entries from the DB.
-### Changed
-- [:exclamation:][0.46.0-bc-request-class-refactor] Big refactor of the `Request` class, removing most custom method implementations.
-
-## [0.45.0] - 2017-06-25
-### Notes
-- [:ledger: View file changes][0.45.0] ∙ [:page_with_curl: DB migration script][0.45.0-sql-migration]
-### Added
-- Documents can be sent by providing its contents via Psr7 stream (as opposed to passing a file path).
-- Allow setting a custom Guzzle HTTP Client for requests (#511, #536).
-- First implementations towards Bots API 3.0.
-### Changed
-- [:exclamation:][0.45.0-bc-chats-params-array] `Request::sendToActiveChats` and `DB::selectChats` now accept parameters as an options array and allow selecting of channels.
-### Deprecated
-- Deprecated `Message::getNewChatMember()` (Use `Message::getNewChatMembers()` instead to get an array of all newly added members).
-### Removed
-- [:exclamation:][0.45.0-bc-up-download-directory] Upload and download directories are not set any more by default and must be set manually.
-- [:exclamation:][0.45.0-bc-remove-deprecated-methods] Completely removed `Telegram::getBotName()` and `Entity::getBotName()` (Use `::getBotUsername()` instead).
-- [:exclamation:][0.45.0-bc-remove-deprecated-methods] Completely removed deprecated `Telegram::unsetWebhook()` (Use `Telegram::deleteWebhook()` instead).
-### Fixed
-- ID fields are now typed with `PARAM_STR` PDO data type, to allow huge numbers.
-- Message type data type for PDO corrected.
-- Indexed table columns now have a fitting length.
-- Take `custom_input` into account when using getUpdates method (mainly for testing).
-- Request limiter has been fixed to correctly support channels.
-
-## [0.44.1] - 2017-04-25
-### Notes
-- [:ledger: View file changes][0.44.1]
-### Fixed
-- Erroneous exception when using webhook without a database connection.
-
-## [0.44.0] - 2017-04-25
-### Notes
-- [:ledger: View file changes][0.44.0]
-### Added
-- Proper standalone `scrutinizer.yml` config.
-- Human-readable `last_error_date_string` for debug command.
-### Changed
-- Bot username no longer required for object instantiation.
-### Removed
-- All examples have been moved to a [dedicated repository][example-bot].
-### Fixed
-- [:exclamation:][0.44.0-bc-update-content-type] Format of Update content type using `$update->getUpdateContent()`.
-
-## [0.43.0] - 2017-04-17
-### Notes
-- [:ledger: View file changes][0.43.0]
-### Added
-- Travis CI webhook for Support Bot.
-- Interval for request limiter.
-- `isRunCommands()` method to check if called via `runCommands()`.
-- Ensure coding standards for `tests` folder with `phpcs`.
-### Changed
-- Move default commands to `examples` folder.
-- All links point to new organisation repo.
-- Add PHP 7.1 support and update dependencies.
-### Fixed
-- Prevent handling the same Telegram updates multiple times, throw exception instead.
-
-## [0.42.0] - 2017-04-09
-### Notes
-- [:ledger: View file changes][0.42.0]
-### Added
-- Added `getBotId()` to directly access bot ID.
-### Changed
-- Rename `bot_name` to `bot_username` everywhere.
-### Deprecated
-- Deprecated `Telegram::getBotName()` (Use `Telegram::getBotUsername()` instead).
-### Fixed
-- Tests are more reliable now, using a properly formatted API key.
-
-## [0.41.0] - 2017-03-25
-### Notes
-- [:ledger: View file changes][0.41.0]
-### Added
-- `$show_in_help` attribute for commands, to set if it should be displayed in the `/help` command.
-- Link to new Telegram group: `https://telegram.me/PHP_Telegram_Bot_Support`
-- Introduce change log.
-
-## [0.40.1] - 2017-03-07
-### Notes
-- [:ledger: View file changes][0.40.1]
-### Fixed
-- Infinite message loop, caused by incorrect Entity variable.
-
-## [0.40.0] - 2017-02-20
-### Notes
-- [:ledger: View file changes][0.40.0]
-### Added
-- Request limiter for incoming requests.
-### Fixed
-- Faulty formatting in logger.
-
-## [0.39.0] - 2017-01-20
-### Notes
-- [:ledger: View file changes][0.39.0]
-### Added
-- Newest bot API changes.
-- Allow direct access to PDO object (`DB::getPdo()`).
-- Simple `/debug` command that displays various system information to help debugging.
-- Crontab-friendly script.
-### Changed
-- Botan integration improvements.
-- Make logger more flexible.
-### Fixed
-- Various bugs and recommendations by Scrutinizer.
-
-## [0.38.1] - 2016-12-25
-### Notes
-- [:ledger: View file changes][0.38.1]
-### Fixed
-- Usage of self-signed certificates in conjunction with the new `allowed_updates` webhook parameter.
-
-## [0.38.0] - 2016-12-25
-### Notes
-- [:ledger: View file changes][0.38.0]
-### Added
-- New `switch_inline_query_current_chat` option for inline keyboard.
-- Support for `channel_post` and `edited_channel_post`.
-- New alias `deleteWebhook` (for `unsetWebhook`).
-### Changed
-- Update WebhookInfo entity and `setWebhook` to allow passing of new arguments.
-
-## [0.37.1] - 2016-12-24
-### Notes
-- [:ledger: View file changes][0.37.1]
-### Fixed
-- Keyboards that are built without using the KeyboardButton objects.
-- Commands that are called via `/command@botname` by correctly passing them the bot name.
-
-## [0.37.0] - 2016-12-13
-### Notes
-- [:ledger: View file changes][0.37.0]
-### Changed
-- Logging improvements to Botan integration.
-### 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
-[0.80.0-bc-commands-with-underscores]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#commands-with-underscores
-[0.78.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.77.1-0.78.0.sql
-[0.77.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.76.1-0.77.0.sql
-[0.75.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.74.0-0.75.0.sql
-[0.75.0-bc-removed-chatactions]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#removed-deprecated-chatactions
-[0.74.0-bc-chatmember-subentities]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#chatmember-subentities
-[0.73.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.72.0-0.73.0.sql
-[0.72.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.71.0-0.72.0.sql
-[0.70.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.64.0-0.70.0.sql
-[0.70.0-bc-minimum-php-73]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#minimum-php-73
-[0.63.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.62.0-0.63.0.sql
-[0.63.0-bc-static-method-entityescapemarkdown]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#static-method-entityescapemarkdown
-[0.62.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.61.1-0.62.0.sql
-[0.61.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.60.0-0.61.0.sql
-[0.61.0-bc-remove-monolog-from-core]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#remove-monolog-from-core
-[0.58.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.57.0-0.58.0.sql
-[0.58.0-bc-return-value-of-empty-entity-properties]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#return-value-of-empty-entity-properties
-[0.58.0-bc-startcommand-is-now-a-usercommand]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#startcommand-is-now-a-usercommand
-[0.57.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.56.0-0.57.0.sql
-[0.55.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.54.1-0.55.0.sql
-[0.55.0-bc-move-animation-out-of-games-namespace]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#move-animation-out-of-games-namespace
-[0.54.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.53.0-0.54.0.sql
-[0.54.0-bc-rename-constants]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#rename-constants
-[0.53.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.52.0-0.53.0.sql
-[0.51.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.50.0-0.51.0.sql
-[0.50.0-bc-messagegetcommand-return-value]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#messagegetcommand-return-value
-[0.48.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/master/utils/db-schema-update/0.47.1-0.48.0.sql
-[0.48.0-bc-correct-printerror]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#correct-printerror
-[0.47.0-bc-private-only-admin-commands]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#private-only-admin-commands
-[0.46.0-bc-request-class-refactor]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#request-class-refactor
-[0.46.0-sql-migration]: https://github.com/php-telegram-bot/core/tree/0.45.0/utils/db-schema-update/0.44.1-0.45.0.sql
-[0.45.0-bc-remove-deprecated-methods]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#remove-deprecated-methods
-[0.45.0-bc-chats-params-array]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#chats-params-array
-[0.45.0-bc-up-download-directory]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#up-download-directory
-[0.44.0-bc-update-content-type]: https://github.com/php-telegram-bot/core/wiki/Breaking-backwards-compatibility#update-getupdatecontent
-[example-bot]: https://github.com/php-telegram-bot/example-bot
-[PSR-3]: https://www.php-fig.org/psr/psr-3
-[Tidelift]: https://tidelift.com/subscription/pkg/packagist-longman-telegram-bot?utm_source=packagist-longman-telegram-bot&utm_medium=referral&utm_campaign=changelog
-
-[Unreleased]: https://github.com/php-telegram-bot/core/compare/master...develop
-[0.82.0]: https://github.com/php-telegram-bot/core/compare/0.81.0...0.82.0
-[0.81.0]: https://github.com/php-telegram-bot/core/compare/0.80.0...0.81.0
-[0.80.0]: https://github.com/php-telegram-bot/core/compare/0.79.0...0.80.0
-[0.79.0]: https://github.com/php-telegram-bot/core/compare/0.78.0...0.79.0
-[0.78.0]: https://github.com/php-telegram-bot/core/compare/0.77.1...0.78.0
-[0.77.1]: https://github.com/php-telegram-bot/core/compare/0.77.0...0.77.1
-[0.77.0]: https://github.com/php-telegram-bot/core/compare/0.76.1...0.77.0
-[0.76.1]: https://github.com/php-telegram-bot/core/compare/0.76.0...0.76.1
-[0.76.0]: https://github.com/php-telegram-bot/core/compare/0.75.0...0.76.0
-[0.75.0]: https://github.com/php-telegram-bot/core/compare/0.74.0...0.75.0
-[0.74.0]: https://github.com/php-telegram-bot/core/compare/0.73.1...0.74.0
-[0.73.1]: https://github.com/php-telegram-bot/core/compare/0.73.0...0.73.1
-[0.73.0]: https://github.com/php-telegram-bot/core/compare/0.72.0...0.73.0
-[0.72.0]: https://github.com/php-telegram-bot/core/compare/0.71.0...0.72.0
-[0.71.0]: https://github.com/php-telegram-bot/core/compare/0.70.1...0.71.0
-[0.70.1]: https://github.com/php-telegram-bot/core/compare/0.70.0...0.70.1
-[0.70.0]: https://github.com/php-telegram-bot/core/compare/0.64.0...0.70.0
-[0.64.0]: https://github.com/php-telegram-bot/core/compare/0.63.1...0.64.0
-[0.63.1]: https://github.com/php-telegram-bot/core/compare/0.63.0...0.63.1
-[0.63.0]: https://github.com/php-telegram-bot/core/compare/0.62.0...0.63.0
-[0.62.0]: https://github.com/php-telegram-bot/core/compare/0.61.1...0.62.0
-[0.61.1]: https://github.com/php-telegram-bot/core/compare/0.61.0...0.61.1
-[0.61.0]: https://github.com/php-telegram-bot/core/compare/0.60.0...0.61.0
-[0.60.0]: https://github.com/php-telegram-bot/core/compare/0.59.1...0.60.0
-[0.59.1]: https://github.com/php-telegram-bot/core/compare/0.59.0...0.59.1
-[0.59.0]: https://github.com/php-telegram-bot/core/compare/0.58.0...0.59.0
-[0.58.0]: https://github.com/php-telegram-bot/core/compare/0.57.0...0.58.0
-[0.57.0]: https://github.com/php-telegram-bot/core/compare/0.56.0...0.57.0
-[0.56.0]: https://github.com/php-telegram-bot/core/compare/0.55.1...0.56.0
-[0.55.1]: https://github.com/php-telegram-bot/core/compare/0.55.0...0.55.1
-[0.55.0]: https://github.com/php-telegram-bot/core/compare/0.54.1...0.55.0
-[0.54.1]: https://github.com/php-telegram-bot/core/compare/0.54.0...0.54.1
-[0.54.0]: https://github.com/php-telegram-bot/core/compare/0.53.0...0.54.0
-[0.53.0]: https://github.com/php-telegram-bot/core/compare/0.52.0...0.53.0
-[0.52.0]: https://github.com/php-telegram-bot/core/compare/0.51.0...0.52.0
-[0.51.0]: https://github.com/php-telegram-bot/core/compare/0.50.0...0.51.0
-[0.50.0]: https://github.com/php-telegram-bot/core/compare/0.49.0...0.50.0
-[0.49.0]: https://github.com/php-telegram-bot/core/compare/0.48.0...0.49.0
-[0.48.0]: https://github.com/php-telegram-bot/core/compare/0.47.1...0.48.0
-[0.47.1]: https://github.com/php-telegram-bot/core/compare/0.47.0...0.47.1
-[0.47.0]: https://github.com/php-telegram-bot/core/compare/0.46.0...0.47.0
-[0.46.0]: https://github.com/php-telegram-bot/core/compare/0.45.0...0.46.0
-[0.45.0]: https://github.com/php-telegram-bot/core/compare/0.44.1...0.45.0
-[0.44.1]: https://github.com/php-telegram-bot/core/compare/0.44.0...0.44.1
-[0.44.0]: https://github.com/php-telegram-bot/core/compare/0.43.0...0.44.0
-[0.43.0]: https://github.com/php-telegram-bot/core/compare/0.42.0...0.43.0
-[0.42.0]: https://github.com/php-telegram-bot/core/compare/0.41.0...0.42.0
-[0.41.0]: https://github.com/php-telegram-bot/core/compare/0.40.1...0.41.0
-[0.40.1]: https://github.com/php-telegram-bot/core/compare/0.40.0...0.40.1
-[0.40.0]: https://github.com/php-telegram-bot/core/compare/0.39.0...0.40.0
-[0.39.0]: https://github.com/php-telegram-bot/core/compare/0.38.1...0.39.0
-[0.38.1]: https://github.com/php-telegram-bot/core/compare/0.38.0...0.38.1
-[0.38.0]: https://github.com/php-telegram-bot/core/compare/0.37.1...0.38.0
-[0.37.1]: https://github.com/php-telegram-bot/core/compare/0.37.0...0.37.1
-[0.37.0]: https://github.com/php-telegram-bot/core/compare/0.36...0.37.0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 5e18c7b73..000000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Contributing
-
-The easiest way to contribute is to work on a checkout of your own fork.
-When working on the `core` repository, it makes sense to rename your fork to `php-telegram-bot`.
-
-Before you contribute code, please make sure it conforms to the PSR-12 coding standard and that the unit tests still pass.
-You can run the following commands to check if everything is ready to submit:
-
-```bash
-cd php-telegram-bot
-composer install
-composer check-code
-```
-
-Which should give you no output, indicating that there are no coding standard errors.
-And then (remember to set up your test database!):
-
-```bash
-composer test
-```
-
-Which should give you no failures or errors. You can ignore any skipped tests as these are for external tools.
-
-## Pushing
-
-Development is based on the git flow branching model (see http://nvie.com/posts/a-successful-git-branching-model/)
-If you fix a bug please push in hotfix branch.
-If you develop a new feature please create a new branch.
-
-## Version
-
-Version number: 0.#version.#hotfix
-
-## Further code convention adopted
-
-- Each method and class is documented with a docblock
-
-Example for a function or method:
-```php
-/**
- * Get formatted date
- *
- * @param string $location
- *
- * @return string
- */
-```
-
-- Each file is provided with the following header:
-```php
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-```
diff --git a/CREDITS b/CREDITS
deleted file mode 100644
index 2214a4c68..000000000
--- a/CREDITS
+++ /dev/null
@@ -1,27 +0,0 @@
-    This is at least a partial credits-file of people that have
-    contributed to the current project. It is sorted by name and
-    formatted to allow easy grepping and beautification by
-    scripts. The fields are: name (N), email (E), web-address
-    (W) and description (D).
-    Thanks,
-
-            Avtandil Kikabidze
-----------
-
-N: Avtandil Kikabidze aka LONGMAN
-E: akalongman@gmail.com
-W: http://longman.me
-D: Project owner, Maintainer
-
-N: Marco Boretto
-E: marco.bore@gmail.com
-D: Maintainer and Collaborator
-
-N: Armando Lüscher
-E: armando@noplanman.ch
-W: http://noplanman.ch
-D: Maintainer and Collaborator
-
-N: Jack'lul (alias)
-E: jacklulcat@gmail.com
-D: Maintainer and Collaborator
diff --git a/LICENSE b/LICENSE.txt
similarity index 87%
rename from LICENSE
rename to LICENSE.txt
index 5fa6ddc85..c13f99117 100644
--- a/LICENSE
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
-The [MIT License](http://opensource.org/licenses/mit-license.php)
+MIT License
 
-Copyright (c) 2015 [Avtandil Kikabidze aka LONGMAN](https://github.com/akalongman)
+Copyright (c) 2024
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 SOFTWARE.
-
diff --git a/README.md b/README.md
index 8be069764..82b079ba7 100644
--- a/README.md
+++ b/README.md
@@ -1,725 +1,190 @@
-<h1 align="center">
-    PHP Telegram Bot<br>
-	<br>
-    <img src="https://raw.githubusercontent.com/php-telegram-bot/assets/master/logo/512px/logo_plain.png" title="PHP Telegram Bot" alt="PHP Telegram Bot logo">
-	<br>
-</h1>
-
-A Telegram Bot based on the official [Telegram Bot API]
-
-[![API Version](https://img.shields.io/badge/Bot%20API-6.9%20%28September%202023%29-32a2da.svg)](https://core.telegram.org/bots/api-changelog#september-22-2023)
-[![Join the bot support group on Telegram](https://img.shields.io/badge/telegram-@PHP__Telegram__Bot__Support-64659d.svg)](https://telegram.me/PHP_Telegram_Bot_Support)
-[![Donate](https://img.shields.io/badge/%F0%9F%92%99-Donate%20%2F%20Support%20Us-blue.svg)](#donate)
-
-[![Tests](https://github.com/php-telegram-bot/core/actions/workflows/tests.yaml/badge.svg)](https://github.com/php-telegram-bot/core/actions/workflows/tests.yaml)
-[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/php-telegram-bot/core/master.svg?style=flat)](https://scrutinizer-ci.com/g/php-telegram-bot/core/?b=master)
-[![Code Quality](https://img.shields.io/scrutinizer/g/php-telegram-bot/core/master.svg?style=flat)](https://scrutinizer-ci.com/g/php-telegram-bot/core/?b=master)
-[![Latest Stable Version](https://img.shields.io/packagist/v/longman/telegram-bot.svg)](https://packagist.org/packages/longman/telegram-bot)
-[![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%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
-- [Introduction](#introduction)
-- [Instructions](#instructions)
-    - [Create your first bot](#create-your-first-bot)
-    - [Require this package with Composer](#require-this-package-with-composer)
-    - [Choose how to retrieve Telegram updates](#choose-how-to-retrieve-telegram-updates)
-- [Using a custom Bot API server](#using-a-custom-bot-api-server)
-- [Webhook installation](#webhook-installation)
-    - [Self Signed Certificate](#self-signed-certificate)
-    - [Unset Webhook](#unset-webhook)
-- [getUpdates installation](#getupdates-installation)
-    - [getUpdates without database](#getupdates-without-database)
-- [Filter Update](#filter-update)
-- [Support](#support)
-    - [Types](#types)
-    - [Inline Query](#inline-query)
-    - [Methods](#methods)
-    - [Send Message](#send-message)
-    - [Send Photo](#send-photo)
-    - [Send Chat Action](#send-chat-action)
-    - [getUserProfilePhoto](#getuserprofilephoto)
-    - [getFile and downloadFile](#getfile-and-downloadfile)
-    - [Send message to all active chats](#send-message-to-all-active-chats)
-- [Utils](#utils)
-    - [MySQL storage (Recommended)](#mysql-storage-recommended)
-        - [External Database connection](#external-database-connection)
-    - [Channels Support](#channels-support)
-- [Commands](#commands)
-    - [Predefined Commands](#predefined-commands)
-    - [Custom Commands](#custom-commands)
-    - [Commands Configuration](#commands-configuration)
-    - [Admin Commands](#admin-commands)
-        - [Set Admins](#set-admins)
-        - [Channel Administration](#channel-administration)
-- [Upload and Download directory path](#upload-and-download-directory-path)
-- [Logging](doc/01-utils.md)
-- [Documentation](#documentation)
-- [Assets](#assets)
-- [Example bot](#example-bot)
-- [Projects with this library](#projects-with-this-library)
-- [Troubleshooting](#troubleshooting)
-- [Contributing](#contributing)
-- [Security](#security)
-- [Donate](#donate)
-- [For enterprise](#for-enterprise)
-- [License](#license)
-- [Credits](#credits)
-
-## Introduction
-
-This is a pure PHP Telegram Bot, fully extensible via plugins.
-
-Telegram announced official support for a [Bot API](https://telegram.org/blog/bot-revolution), allowing integrators of all sorts to bring automated interactions to the mobile platform.
-This Bot aims to provide a platform where one can simply write a bot and have interactions in a matter of minutes.
-
-The Bot can:
-- Retrieve updates with [webhook](#webhook-installation) and [getUpdates](#getupdates-installation) methods.
-- Supports all types and methods according to Telegram Bot API 6.9 (September 2023).
-- Supports supergroups.
-- Handle commands in chat with other bots.
-- Manage Channel from the bot admin interface.
-- Full support for **inline bots**.
-- Inline keyboard.
-- Messages, InlineQuery and ChosenInlineQuery are stored in the Database.
-- Conversation feature.
-
----
-
-This code is available on [GitHub](https://github.com/php-telegram-bot/core). Pull requests are welcome.
-
-## Instructions
-
-### Create your first bot
-
-1. Message [`@BotFather`](https://telegram.me/BotFather) with the following text: `/newbot`
-
-   If you don't know how to message by username, click the search field on your Telegram app and type `@BotFather`, where you should be able to initiate a conversation. Be careful not to send it to the wrong contact, because some users have similar usernames to `BotFather`.
-
-   ![BotFather initial conversation](https://user-images.githubusercontent.com/9423417/60736229-bc2aeb80-9f45-11e9-8d35-5b53145347bc.png)
-
-2. `@BotFather` replies with:
-
-    ```
-    Alright, a new bot. How are we going to call it? Please choose a name for your bot.
-    ```
-
-3. Type whatever name you want for your bot.
-
-4. `@BotFather` replies with:
-
-    ```
-    Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot.
-    ```
-
-5. Type whatever username you want for your bot, minimum 5 characters, and must end with `bot`. For example: `telesample_bot`
-
-6. `@BotFather` replies with:
-
-    ```
-    Done! Congratulations on your new bot. You will find it at
-    telegram.me/telesample_bot. You can now add a description, about
-    section and profile picture for your bot, see /help for a list of
-    commands.
+<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
+<a name="readme-top"></a>
 
-    Use this token to access the HTTP API:
-    123456789:AAG90e14-0f8-40183D-18491dDE
 
-    For a description of the Bot API, see this page:
-    https://core.telegram.org/bots/api
-    ```
 
-7. Note down the 'token' mentioned above.
+<!-- PROJECT SHIELDS -->
+[![Contributors][contributors-shield]][contributors-url]
+[![Forks][forks-shield]][forks-url]
+[![Stargazers][stars-shield]][stars-url]
+[![Issues][issues-shield]][issues-url]
+[![MIT License][license-shield]][license-url]
 
-*Optionally set the bot privacy:*
 
-1. Send `/setprivacy` to `@BotFather`.
 
-   ![BotFather later conversation](https://user-images.githubusercontent.com/9423417/60736340-26439080-9f46-11e9-970f-8f33bbe39c5f.png)
+<!-- PROJECT LOGO -->
+<br />
+<div align="center">
+  <a href="https://github.com/php-telegram-bot/core">
+    <img src="logo.png" alt="Php Telegram Bot Logo" width="128" height="128">
+  </a>
 
-2. `@BotFather` replies with:
+<h3 align="center">PHP Telegram Bot</h3>
 
-    ```
-    Choose a bot to change group messages settings.
-    ```
+  <p align="center">
+    A Telegram Bot based on the official <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>
+    <br />
+    <br />
+    <a href="https://github.com/php-telegram-bot/core">View Demo</a>
+    ·
+    <a href="https://github.com/php-telegram-bot/core/issues/new?assignees=&labels=bug,v1&projects=&template=Bug.md">Report Bug</a>
+    ·
+    <a href="https://github.com/php-telegram-bot/core/issues/new?assignees=&labels=feature+request,v1&projects=&template=Feature_Request.md">Request Feature</a>
+  </p>
+</div>
 
-3. Type (or select) `@telesample_bot` (change to the username you set at step 5
-above, but start it with `@`)
 
-4. `@BotFather` replies with:
 
-    ```
-    'Enable' - your bot will only receive messages that either start with the '/' symbol or mention the bot by username.
-    'Disable' - your bot will receive all messages that people send to groups.
-    Current status is: ENABLED
-    ```
+<!-- TABLE OF CONTENTS -->
+<details>
+  <summary>Table of Contents</summary>
+  <ol>
+    <li>
+      <a href="#about-the-project">About The Project</a>
+    </li>
+    <li>
+      <a href="#getting-started">Getting Started</a>
+      <ul>
+        <li><a href="#prerequisites">Prerequisites</a></li>
+        <li><a href="#installation">Installation</a></li>
+      </ul>
+    </li>
+    <li><a href="#usage">Usage</a></li>
+    <li><a href="#roadmap">Roadmap</a></li>
+    <li><a href="#contributing">Contributing</a></li>
+    <li><a href="#license">License</a></li>
+    <li><a href="#contact">Contact</a></li>
+    <li><a href="#acknowledgments">Acknowledgments</a></li>
+  </ol>
+</details>
 
-5. Type (or select) `Disable` to let your bot receive all messages sent to a group.
 
-6. `@BotFather` replies with:
 
-    ```
-    Success! The new status is: DISABLED. /help
-    ```
+<!-- ABOUT THE PROJECT -->
 
-### Require this package with Composer
+## About The Project
 
-Install this package through [Composer].
-Edit your project's `composer.json` file to require `longman/telegram-bot`.
+_TODO_
 
-Create *composer.json* file
-```json
-{
-    "name": "yourproject/yourproject",
-    "type": "project",
-    "require": {
-        "php": "^8.1",
-        "longman/telegram-bot": "*"
-    }
-}
-```
-and run `composer update`
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-**or**
 
-run this command in your command line:
 
-```bash
-composer require longman/telegram-bot
-```
+<!-- GETTING STARTED -->
 
-### Choose how to retrieve Telegram updates
+## Getting Started
 
-The bot can handle updates with [**Webhook**](#webhook-installation) or [**getUpdates**](#getupdates-installation) method:
+_TODO_
 
-|      | Webhook | getUpdates |
-| ---- | :----: | :----: |
-| Description | Telegram sends the updates directly to your host | You have to fetch Telegram updates manually |
-| Host with https | Required | Not required |
-| MySQL | Not required | ([Not](#getupdates-without-database)) Required  |
+### Prerequisites
 
-## Using a custom Bot API server
+- Supported PHP Versions: **8.1 - 8.3**
 
-**For advanced users only!**
+### Installation
 
-As from Telegram Bot API 5.0, users can [run their own Bot API server] to handle updates.
-This means, that the PHP Telegram Bot needs to be configured to serve that custom URI.
-Additionally, you can define the URI where uploaded files to the bot can be downloaded (note the `{API_KEY}` placeholder).
+_TODO_
 
-```php
-Longman\TelegramBot\Request::setCustomBotApiUri(
-    $api_base_uri          = 'https://your-bot-api-server', // Default: https://api.telegram.org
-    $api_base_download_uri = '/path/to/files/{API_KEY}'     // Default: /file/bot{API_KEY}
-);
-```
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-**Note:** If you are running your bot in `--local` mode, you won't need the `Request::downloadFile()` method, since you can then access your files directly from the absolute path returned by `Request::getFile()`.
 
-## Webhook installation
 
-Note: For a more detailed explanation, head over to the [example-bot repository] and follow the instructions there.
+<!-- USAGE EXAMPLES -->
 
-In order to set a [Webhook][api-setwebhook] you need a server with HTTPS and composer support.
-(For a [self signed certificate](#self-signed-certificate) you need to add some extra code)
+## Usage
 
-Create *[set.php]* with the following contents:
-```php
-<?php
-// Load composer
-require __DIR__ . '/vendor/autoload.php';
+_TODO_
 
-$bot_api_key  = 'your:bot_api_key';
-$bot_username = 'username_bot';
-$hook_url     = 'https://your-domain/path/to/hook.php';
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-try {
-    // Create Telegram API object
-    $telegram = new Longman\TelegramBot\Telegram($bot_api_key, $bot_username);
 
-    // Set webhook
-    $result = $telegram->setWebhook($hook_url);
-    if ($result->isOk()) {
-        echo $result->getDescription();
-    }
-} catch (Longman\TelegramBot\Exception\TelegramException $e) {
-    // log telegram errors
-    // echo $e->getMessage();
-}
-```
 
-Open your *set.php* via the browser to register the webhook with Telegram.
-You should see `Webhook was set`.
+<!-- ROADMAP -->
 
-Now, create *[hook.php]* with the following contents:
-```php
-<?php
-// Load composer
-require __DIR__ . '/vendor/autoload.php';
+## Roadmap
 
-$bot_api_key  = 'your:bot_api_key';
-$bot_username = 'username_bot';
+- [x] Adding Telegram Types
+- [x] Adding base functionality to call API Methods
+- [x] Adding base functionality to parse Telegram Entities
+- [x] Test editMessageText with its return types Message or bool
+- [x] File Downloads/Uploads
+- [ ] Custom Bot API Server
 
-try {
-    // Create Telegram API object
-    $telegram = new Longman\TelegramBot\Telegram($bot_api_key, $bot_username);
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-    // Handle telegram webhook request
-    $telegram->handle();
-} catch (Longman\TelegramBot\Exception\TelegramException $e) {
-    // Silence is golden!
-    // log telegram errors
-    // echo $e->getMessage();
-}
-```
 
-### Self Signed Certificate
 
-Upload the certificate and add the path as a parameter in *set.php*:
-```php
-$result = $telegram->setWebhook($hook_url, ['certificate' => '/path/to/certificate']);
-```
+<!-- CONTRIBUTING -->
 
-### Unset Webhook
-
-Edit *[unset.php]* with your bot credentials and execute it.
-
-## getUpdates installation
-
-For best performance, the MySQL database should be enabled for the `getUpdates` method!
-
-Create *[getUpdatesCLI.php]* with the following contents:
-```php
-#!/usr/bin/env php
-<?php
-require __DIR__ . '/vendor/autoload.php';
-
-$bot_api_key  = 'your:bot_api_key';
-$bot_username = 'username_bot';
-
-$mysql_credentials = [
-   'host'     => 'localhost',
-   'port'     => 3306, // optional
-   'user'     => 'dbuser',
-   'password' => 'dbpass',
-   'database' => 'dbname',
-];
-
-try {
-    // Create Telegram API object
-    $telegram = new Longman\TelegramBot\Telegram($bot_api_key, $bot_username);
-
-    // Enable MySQL
-    $telegram->enableMySql($mysql_credentials);
-
-    // Handle telegram getUpdates request
-    $telegram->handleGetUpdates();
-} catch (Longman\TelegramBot\Exception\TelegramException $e) {
-    // log telegram errors
-    // echo $e->getMessage();
-}
-```
-
-Next, give the file permission to execute:
-```bash
-$ chmod +x getUpdatesCLI.php
-```
-
-Lastly, run it!
-```bash
-$ ./getUpdatesCLI.php
-```
-
-### getUpdates without database
-
-If you choose to / or are obliged to use the `getUpdates` method without a database, you can replace the `$telegram->enableMySql(...);` line above with:
-```php
-$telegram->useGetUpdatesWithoutDatabase();
-```
-
-## Filter Update
-
-:exclamation: Note that by default, Telegram will send any new update types that may be added in the future. This may cause commands that don't take this into account to break!
-
-It is suggested that you specifically define which update types your bot can receive and handle correctly.
-
-You can define which update types are sent to your bot by defining them when setting the [webhook](#webhook-installation) or passing an array of allowed types when using [getUpdates](#getupdates-installation).
-
-```php
-use Longman\TelegramBot\Entities\Update;
-
-// For all update types currently implemented in this library:
-// $allowed_updates = Update::getUpdateTypes();
-
-// Define the list of allowed Update types manually:
-$allowed_updates = [
-    Update::TYPE_MESSAGE,
-    Update::TYPE_CHANNEL_POST,
-    // etc.
-];
-
-// When setting the webhook.
-$telegram->setWebhook($hook_url, ['allowed_updates' => $allowed_updates]);
-
-// When handling the getUpdates method.
-$telegram->handleGetUpdates(['allowed_updates' => $allowed_updates]);
-```
-
-Alternatively, Update processing can be allowed or denied by defining a custom update filter.
-
-Let's say we only want to allow messages from a user with ID `428`, we can do the following before handling the request:
-
-```php
-$telegram->setUpdateFilter(function (Update $update, Telegram $telegram, &$reason = 'Update denied by update_filter') {
-    $user_id = $update->getMessage()->getFrom()->getId();
-    if ($user_id === 428) {
-        return true;
-    }
-
-    $reason = "Invalid user with ID {$user_id}";
-    return false;
-});
-```
-
-The reason for denying an update can be defined with the `$reason` parameter. This text gets written to the debug log.
-
-## Support
-
-### Types
-
-All types are implemented according to Telegram API 6.9 (September 2023).
-
-### Inline Query
-
-Full support for inline query according to Telegram API 6.9 (September 2023).
-
-### Methods
-
-All methods are implemented according to Telegram API 6.9 (September 2023).
-
-#### Send Message
-
-Messages longer than 4096 characters are split up into multiple messages.
-
-```php
-$result = Request::sendMessage([
-    'chat_id' => $chat_id,
-    'text'    => 'Your utf8 text 😜 ...',
-]);
-```
-
-#### Send Photo
-
-To send a local photo, add it properly to the `$data` parameter using the file path:
-
-```php
-$result = Request::sendPhoto([
-    'chat_id' => $chat_id,
-    'photo'   => Request::encodeFile('/path/to/pic.jpg'),
-]);
-```
-
-If you know the `file_id` of a previously uploaded file, just use it directly in the data array:
-
-```php
-$result = Request::sendPhoto([
-    'chat_id' => $chat_id,
-    'photo'   => 'AAQCCBNtIhAoAAss4tLEZ3x6HzqVAAqC',
-]);
-```
-
-To send a remote photo, use the direct URL instead:
-
-```php
-$result = Request::sendPhoto([
-    'chat_id' => $chat_id,
-    'photo'   => 'https://example.com/path/to/pic.jpg',
-]);
-```
-
-*sendAudio*, *sendDocument*, *sendAnimation*, *sendSticker*, *sendVideo*, *sendVoice* and *sendVideoNote* all work in the same way, just check the [API documentation](https://core.telegram.org/bots/api#sendphoto) for the exact usage.
-See the *[ImageCommand.php]* for a full example.
-
-#### Send Chat Action
-
-```php
-Request::sendChatAction([
-    'chat_id' => $chat_id,
-    'action'  => Longman\TelegramBot\ChatAction::TYPING,
-]);
-```
-
-#### getUserProfilePhoto
-
-Retrieve the user photo. (see *[WhoamiCommand.php]* for a full example)
-
-#### getFile and downloadFile
-
-Get the file path and download it. (see *[WhoamiCommand.php]* for a full example)
-
-#### Send message to all active chats
-
-To do this you have to enable the MySQL connection.
-Here's an example of use (check [`DB::selectChats()`][DB::selectChats] for parameter usage):
-
-```php
-$results = Request::sendToActiveChats(
-    'sendMessage', // Callback function to execute (see Request.php methods)
-    ['text' => 'Hey! Check out the new features!!'], // Param to evaluate the request
-    [
-        'groups'      => true,
-        'supergroups' => true,
-        'channels'    => false,
-        'users'       => true,
-    ]
-);
-```
-
-You can also broadcast a message to users, from the private chat with your bot. Take a look at the [admin commands](#admin-commands) below.
-
-## Utils
-
-### MySQL storage (Recommended)
-
-If you want to save messages/users/chats for further usage in commands, create a new database (`utf8mb4_unicode_520_ci`), import *[structure.sql]* and enable MySQL support BEFORE `handle()` method:
-
-```php
-$mysql_credentials = [
-   'host'     => 'localhost',
-   'port'     => 3306, // optional
-   'user'     => 'dbuser',
-   'password' => 'dbpass',
-   'database' => 'dbname',
-];
-
-$telegram->enableMySql($mysql_credentials);
-```
-
-You can set a custom prefix to all the tables while you are enabling MySQL:
-
-```php
-$telegram->enableMySql($mysql_credentials, $bot_username . '_');
-```
-
-You can also store inline query and chosen inline query data in the database.
-
-#### External Database connection
-
-It is possible to provide the library with an external MySQL PDO connection.
-Here's how to configure it:
-
-```php
-$telegram->enableExternalMySql($external_pdo_connection);
-//$telegram->enableExternalMySql($external_pdo_connection, $table_prefix)
-```
-### Channels Support
-
-All methods implemented can be used to manage channels.
-With [admin commands](#admin-commands) you can manage your channels directly with your bot private chat.
-
-## Commands
-
-### Predefined Commands
-
-The bot is able to recognise commands in a chat with multiple bots (/command@mybot).
-
-It can also execute commands that get triggered by events, so-called Service Messages.
-
-### Custom Commands
-
-Maybe you would like to develop your own commands.
-There is a guide to help you [create your own commands][wiki-create-your-own-commands].
-
-Also, be sure to have a look at the [example commands][ExampleCommands-folder] to learn more about custom commands and how they work.
-
-You can add your custom commands in different ways:
+## Contributing
 
-```php
-// Add a folder that contains command files
-$telegram->addCommandsPath('/path/to/command/files');
-//$telegram->addCommandsPaths(['/path/to/command/files', '/another/path']);
+Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any
+contributions you make are **greatly appreciated**.
 
-// Add a command directly using the class name
-$telegram->addCommandClass(MyCommand::class);
-//$telegram->addCommandClasses([MyCommand::class, MyOtherCommand::class]);
-```
+If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also
+simply open an issue with the tag "enhancement".
+Don't forget to give the project a star! Thanks again!
 
-### Commands Configuration
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
 
-With this method you can set some command specific parameters, for example:
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-```php
-// Google geocode/timezone API key for /date command
-$telegram->setCommandConfig('date', [
-    'google_api_key' => 'your_google_api_key_here',
-]);
 
-// OpenWeatherMap API key for /weather command
-$telegram->setCommandConfig('weather', [
-    'owm_api_key' => 'your_owm_api_key_here',
-]);
-```
 
-### Admin Commands
+<!-- LICENSE -->
 
-Enabling this feature, the bot admin can perform some super user commands like:
-- List all the chats started with the bot */chats*
-- Clean up old database entries */cleanup*
-- Show debug information about the bot */debug*
-- Send message to all chats */sendtoall*
-- Post any content to your channels */sendtochannel*
-- Inspect a user or a chat with */whois*
+## License
 
-Take a look at all default admin commands stored in the *[src/Commands/AdminCommands/][AdminCommands-folder]* folder.
+Distributed under the MIT License. See `LICENSE.txt` for more information.
 
-#### Set Admins
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-You can specify one or more admins with this option:
 
-```php
-// Single admin
-$telegram->enableAdmin(your_telegram_user_id);
 
-// Multiple admins
-$telegram->enableAdmins([
-    your_telegram_user_id,
-    other_telegram_user_id,
-]);
-```
-Telegram user id can be retrieved with the *[/whoami][WhoamiCommand.php]* command.
+<!-- CONTACT -->
 
-#### Channel Administration
+## Contact
 
-To enable this feature follow these steps:
-- Add your bot as channel administrator, this can be done with any Telegram client.
-- Enable admin interface for your user as explained in the admin section above.
-- Enter your channel name as a parameter for the *[/sendtochannel][SendtochannelCommand.php]* command:
-```php
-$telegram->setCommandConfig('sendtochannel', [
-    'your_channel' => [
-        '@type_here_your_channel',
-    ]
-]);
-```
-- If you want to manage more channels:
-```php
-$telegram->setCommandConfig('sendtochannel', [
-    'your_channel' => [
-        '@type_here_your_channel',
-        '@type_here_another_channel',
-        '@and_so_on',
-    ]
-]);
-```
-- Enjoy!
+Tii - [@Tii@chaos.social](https://chaos.social/@Tii)
 
-## Upload and Download directory path
+Project Link: [https://github.com/php-telegram-bot/core](https://github.com/php-telegram-bot/core)
 
-To use the Upload and Download functionality, you need to set the paths with:
-```php
-$telegram->setDownloadPath('/your/path/Download');
-$telegram->setUploadPath('/your/path/Upload');
-```
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-## Documentation
 
-Take a look at the repo [Wiki] for further information and tutorials!
-Feel free to improve!
 
-## Assets
+<!-- ACKNOWLEDGMENTS -->
 
-All project assets can be found in the [assets](https://github.com/php-telegram-bot/assets) repository.
+## Acknowledgments
 
-## Example bot
+* Big Thanks to [Avtandil Kikabidze aka LONGMAN](https://github.com/akalongman), who created this library
 
-We're busy working on a full A-Z example bot, to help get you started with this library and to show you how to use all its features.
-You can check the progress of the [example-bot repository]).
-
-## Projects with this library
-
-Here's a list of projects that feats this library, feel free to add yours!
-- [Inline Games](https://github.com/jacklul/inlinegamesbot) ([@inlinegamesbot](https://telegram.me/inlinegamesbot))
-- [Super-Dice-Roll](https://github.com/RafaelDelboni/Super-Dice-Roll) ([@superdiceroll_bot](https://telegram.me/superdiceroll_bot))
-- [tg-mentioned-bot](https://github.com/gruessung/tg-mentioned-bot)
-- [OSMdeWikiBot](https://github.com/OSM-de/TelegramWikiBot) ([@OSM_de](https://t.me/OSM_de))
-- [pass-generator-webbot](https://github.com/OxMohsen/pass-generator-webbot)
-- [Chess Quiz Bot](https://github.com/1int/chess-quiz-bot)
-- [PHP Telegram Bot - Symfony Bundle](https://github.com/m4n50n/telegram_bot_bundle)
-
-## Troubleshooting
-
-If you like living on the edge, please report any bugs you find on the [PHP Telegram Bot issues][issues] page.
+<p align="right">(<a href="#readme-top">back to top</a>)</p>
 
-## Contributing
 
-See [CONTRIBUTING](CONTRIBUTING.md) for more information.
 
-## Security
+<!-- MARKDOWN LINKS & IMAGES -->
+<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
 
-See [SECURITY](SECURITY.md) for more information.
+[contributors-shield]: https://img.shields.io/github/contributors/php-telegram-bot/core.svg?style=for-the-badge
 
-## Donate
+[contributors-url]: https://github.com/php-telegram-bot/core/graphs/contributors
 
-All work on this bot consists of many hours of coding during our free time, to provide you with a Telegram Bot library that is easy to use and extend.
-If you enjoy using this library and would like to say thank you, donations are a great way to show your support.
+[forks-shield]: https://img.shields.io/github/forks/php-telegram-bot/core.svg?style=for-the-badge
 
-Donations are invested back into the project :+1:
+[forks-url]: https://github.com/php-telegram-bot/core/network/members
 
-Thank you for keeping this project alive :pray:
+[stars-shield]: https://img.shields.io/github/stars/php-telegram-bot/core.svg?style=for-the-badge
 
-- [![Patreon](https://user-images.githubusercontent.com/9423417/59235980-a5fa6b80-8be3-11e9-8ae7-020bc4ae9baa.png) Patreon.com/phptelegrambot][Patreon]
-- [![OpenCollective](https://user-images.githubusercontent.com/9423417/59235978-a561d500-8be3-11e9-89be-82ec54be1546.png) OpenCollective.com/php-telegram-bot][OpenCollective]
-- [![Ko-fi](https://user-images.githubusercontent.com/9423417/59235976-a561d500-8be3-11e9-911d-b1908c3e6a33.png) Ko-fi.com/phptelegrambot][Ko-fi]
-- [![Tidelift](https://user-images.githubusercontent.com/9423417/59235982-a6930200-8be3-11e9-8ac2-bfb6991d80c5.png) Tidelift.com/longman/telegram-bot][Tidelift]
-- [![Liberapay](https://user-images.githubusercontent.com/9423417/59235977-a561d500-8be3-11e9-9d16-bc3b13d3ceba.png) Liberapay.com/PHP-Telegram-Bot][Liberapay]
-- [![PayPal](https://user-images.githubusercontent.com/9423417/59235981-a5fa6b80-8be3-11e9-9761-15eb7a524cb0.png) PayPal.me/noplanman][PayPal-noplanman] (account of @noplanman)
-- [![Bitcoin](https://user-images.githubusercontent.com/9423417/59235974-a4c93e80-8be3-11e9-9fde-260c821b6eae.png) 166NcyE7nDxkRPWidWtG1rqrNJoD5oYNiV][Bitcoin]
-- [![Ethereum](https://user-images.githubusercontent.com/9423417/59235975-a4c93e80-8be3-11e9-8762-7a47c62c968d.png) 0x485855634fa212b0745375e593fAaf8321A81055][Ethereum]
+[stars-url]: https://github.com/php-telegram-bot/core/stargazers
 
-## For enterprise
+[issues-shield]: https://img.shields.io/github/issues/php-telegram-bot/core.svg?style=for-the-badge
 
-Available as part of the Tidelift Subscription.
+[issues-url]: https://github.com/php-telegram-bot/core/issues
 
-The maintainers of `PHP Telegram Bot` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.][Tidelift]
-
-## License
+[license-shield]: https://img.shields.io/github/license/php-telegram-bot/core.svg?style=for-the-badge
 
-Please see the [LICENSE](LICENSE) included in this repository for a full copy of the MIT license, which this project is licensed under.
-
-## Credits
-
-Credit list in [CREDITS](CREDITS)
-
----
-
-[Telegram Bot API]: https://core.telegram.org/bots/api "Telegram Bot API"
-[Composer]: https://getcomposer.org/ "Composer"
-[run their own Bot API server]: https://core.telegram.org/bots/api#using-a-local-bot-api-server "Using a Local Bot API Server"
-[example-bot repository]: https://github.com/php-telegram-bot/example-bot "Example Bot repository"
-[api-setwebhook]: https://core.telegram.org/bots/api#setwebhook "Webhook on Telegram Bot API"
-[set.php]: https://github.com/php-telegram-bot/example-bot/blob/master/set.php "example set.php"
-[unset.php]: https://github.com/php-telegram-bot/example-bot/blob/master/unset.php "example unset.php"
-[hook.php]: https://github.com/php-telegram-bot/example-bot/blob/master/hook.php "example hook.php"
-[getUpdatesCLI.php]: https://github.com/php-telegram-bot/example-bot/blob/master/getUpdatesCLI.php "example getUpdatesCLI.php"
-[AdminCommands-folder]: https://github.com/php-telegram-bot/core/tree/master/src/Commands/AdminCommands "Admin commands folder"
-[ExampleCommands-folder]: https://github.com/php-telegram-bot/example-bot/blob/master/Commands "Example commands folder"
-[ImageCommand.php]: https://github.com/php-telegram-bot/example-bot/blob/master/Commands/Other/ImageCommand.php "example /image command"
-[WhoamiCommand.php]: https://github.com/php-telegram-bot/example-bot/blob/master/Commands/WhoamiCommand.php "example /whoami command"
-[HelpCommand.php]: https://github.com/php-telegram-bot/example-bot/blob/master/Commands/HelpCommand.php "example /help command"
-[SendtochannelCommand.php]: https://github.com/php-telegram-bot/core/blob/master/src/Commands/AdminCommands/SendtochannelCommand.php "/sendtochannel admin command"
-[DB::selectChats]: https://github.com/php-telegram-bot/core/blob/0.70.0/src/DB.php#L1148 "DB::selectChats() parameters"
-[structure.sql]: https://github.com/php-telegram-bot/core/blob/master/structure.sql "DB structure for importing"
-[Wiki]: https://github.com/php-telegram-bot/core/wiki "PHP Telegram Bot Wiki"
-[wiki-create-your-own-commands]: https://github.com/php-telegram-bot/core/wiki/Create-your-own-commands "Create your own commands"
-[issues]: https://github.com/php-telegram-bot/core/issues "PHP Telegram Bot Issues"
-
-[Patreon]: https://www.patreon.com/phptelegrambot "Support us on Patreon"
-[OpenCollective]: https://opencollective.com/php-telegram-bot "Support us on Open Collective"
-[Ko-fi]: https://ko-fi.com/phptelegrambot "Support us on Ko-fi"
-[Tidelift]: https://tidelift.com/subscription/pkg/packagist-longman-telegram-bot?utm_source=packagist-longman-telegram-bot&utm_medium=referral&utm_campaign=enterprise&utm_term=repo "Learn more about the Tidelift Subscription"
-[Liberapay]: https://liberapay.com/PHP-Telegram-Bot "Donate with Liberapay"
-[PayPal-noplanman]: https://paypal.me/noplanman "Donate with PayPal"
-[Bitcoin]: https://www.blockchain.com/btc/address/166NcyE7nDxkRPWidWtG1rqrNJoD5oYNiV "Donate with Bitcoin"
-[Ethereum]: https://etherscan.io/address/0x485855634fa212b0745375e593fAaf8321A81055 "Donate with Ethereum"
+[license-url]: https://github.com/php-telegram-bot/core/blob/master/LICENSE.txt
\ No newline at end of file
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 5358dc50b..000000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Security Policy
-
-To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure.
diff --git a/composer.json b/composer.json
index 1c421ec93..7c7126202 100644
--- a/composer.json
+++ b/composer.json
@@ -1,81 +1,77 @@
 {
-    "name": "longman/telegram-bot",
-    "type": "library",
-    "description": "PHP Telegram bot",
-    "keywords": ["telegram", "bot", "api"],
-    "license": "MIT",
-    "homepage": "https://github.com/php-telegram-bot/core",
-    "support": {
-        "issues": "https://github.com/php-telegram-bot/core/issues",
-        "source": "https://github.com/php-telegram-bot/core"
+  "name": "php-telegram-bot/core",
+  "type": "library",
+  "description": "PHP Telegram Bot",
+  "keywords": [
+    "telegram",
+    "bot",
+    "api"
+  ],
+  "license": "MIT",
+  "homepage": "https://github.com/php-telegram-bot/core",
+  "support": {
+    "issues": "https://github.com/php-telegram-bot/core/issues",
+    "source": "https://github.com/php-telegram-bot/core"
+  },
+  "authors": [
+    {
+      "name": "Avtandil Kikabidze aka LONGMAN",
+      "email": "akalongman@gmail.com",
+      "homepage": "http://longman.me",
+      "role": "Developer"
     },
-    "authors": [
-        {
-            "name": "Avtandil Kikabidze aka LONGMAN",
-            "email": "akalongman@gmail.com",
-            "homepage": "http://longman.me",
-            "role": "Developer"
-        },
-        {
-            "name": "Armando Lüscher",
-            "email": "armando@noplanman.ch",
-            "homepage": "https://noplanman.ch",
-            "role": "Developer"
-        },
-        {
-            "name": "PHP Telegram Bot Team",
-            "homepage": "https://github.com/php-telegram-bot/core/graphs/contributors",
-            "role": "Developer"
-        }
-    ],
-    "require": {
-        "php": "^8.1",
-        "ext-pdo": "*",
-        "ext-curl": "*",
-        "ext-json": "*",
-        "ext-mbstring": "*",
-        "psr/log": "^1.1|^2.0|^3.0",
-        "guzzlehttp/guzzle": "^6.0|^7.0"
+    {
+      "name": "Armando Lüscher",
+      "email": "armando@noplanman.ch",
+      "homepage": "https://noplanman.ch",
+      "role": "Developer"
     },
-    "require-dev": {
-        "phpunit/phpunit": "^9.5",
-        "squizlabs/php_codesniffer": "^3.6",
-        "dms/phpunit-arraysubset-asserts": "^0.2",
-        "monolog/monolog": "^2.1"
+    {
+      "name": "Tii",
+      "email": "mail@tii.one",
+      "role": "Developer"
     },
-    "autoload": {
-        "psr-4": {
-            "Longman\\TelegramBot\\": "src"
-        }
-    },
-    "autoload-dev": {
-        "psr-4": {
-            "Longman\\TelegramBot\\Tests\\Unit\\": "tests/Unit"
-        }
-    },
-    "scripts": {
-        "check-code": [
-            "\"vendor/bin/phpcs\""
-        ],
-        "test": [
-            "\"vendor/bin/phpunit\""
-        ],
-        "test-cov": [
-            "XDEBUG_MODE=coverage \"vendor/bin/phpunit\" --coverage-clover clover.xml"
-        ],
-        "test-cov-upload": [
-            "@php ocular.phar code-coverage:upload --format=php-clover clover.xml"
-        ]
-    },
-    "config": {
-        "optimize-autoloader": true,
-        "preferred-install": "dist",
-        "sort-packages": true,
-        "process-timeout": 3600
+    {
+      "name": "PHP Telegram Bot Team",
+      "homepage": "https://github.com/php-telegram-bot/core/graphs/contributors",
+      "role": "Developer"
+    }
+  ],
+  "require": {
+    "php": "^8.1",
+    "php-http/discovery": "^1.17",
+    "php-http/multipart-stream-builder": "^1.3",
+    "psr/http-client": "*",
+    "psr/http-client-implementation": "*",
+    "psr/http-message": "*"
+  },
+  "require-dev": {
+    "guzzlehttp/psr7": "^2.6",
+    "laravel/pint": "^1.16",
+    "php-http/guzzle7-adapter": "^1.0",
+    "spatie/ray": "^1.41",
+    "vlucas/phpdotenv": "^5.6"
+  },
+  "autoload": {
+    "psr-4": {
+      "PhpTelegramBot\\Core\\": "src"
     },
-    "extra": {
-        "branch-alias": {
-            "dev-develop": "0.83.x-dev"
-        }
+    "files": [
+      "src/helpers.php"
+    ]
+  },
+  "config": {
+    "optimize-autoloader": true,
+    "preferred-install": "dist",
+    "sort-packages": true,
+    "process-timeout": 3600,
+    "allow-plugins": {
+      "php-http/discovery": false
+    }
+  },
+  "extra": {
+    "branch-alias": {
+      "dev-v1": "1.x-dev"
     }
+  }
 }
diff --git a/doc/01-utils.md b/doc/01-utils.md
deleted file mode 100644
index e741d3cfc..000000000
--- a/doc/01-utils.md
+++ /dev/null
@@ -1,58 +0,0 @@
-## Logging
-PHP Telegram Bot library features [PSR-3] compatible logging to store logs.
-
-You can find a list of compatible packages that can be used on [Packagist][PSR-3-providers].
-
-Logs are divided into the following streams:
-- `error`: Collects all the exceptions thrown by the library.
-- `debug`: Stores requests made to the Telegram API, useful for debugging.
-- `update`: Incoming raw updates (JSON string from Webhook and getUpdates).
-
-### Initialisation
-To initialise the logger, you can pass any `LoggerInterface` objects to the `TelegramLog::initialize` method.
-
-The first parameter is the main logger, the second one is used for the raw updates.
-
-(in this example we're using [Monolog])
-```php
-use Longman\TelegramBot\TelegramLog;
-use Monolog\Formatter\LineFormatter;
-use Monolog\Handler\StreamHandler;
-use Monolog\Logger;
-
-TelegramLog::initialize(
-    // Main logger that handles all 'debug' and 'error' logs.
-    new Logger('telegram_bot', [
-        (new StreamHandler('/path/to/debug_log_file', Logger::DEBUG))->setFormatter(new LineFormatter(null, null, true)),
-        (new StreamHandler('/path/to/error_log_file', Logger::ERROR))->setFormatter(new LineFormatter(null, null, true)),
-    ]),
-    // Updates logger for raw updates.
-    new Logger('telegram_bot_updates', [
-        (new StreamHandler('/path/to/updates_log_file', Logger::INFO))->setFormatter(new LineFormatter('%message%' . PHP_EOL)),
-    ])
-);
-```
-
-### Raw data
-Why do I need to log the raw updates?
-Telegram API changes continuously and it often happens that the database schema is not up to date with new entities/features. So it can happen that your table schema doesn't allow storing new valuable information coming from Telegram.
-
-If you store the raw data you can import all updates on the newest table schema by simply using [this script](../utils/importFromLog.php).
-Remember to always backup first!!
-
-### Always log request and response data
-If you'd like to always log the request and response data to the debug log, even for successful requests, you can set the appropriate variable:
-```php
-\Longman\TelegramBot\TelegramLog::$always_log_request_and_response = true;
-```
-
-### Hiding API token from the log
-By default, the API token is removed from the log, to prevent any mistaken leakage when posting logs online.
-This behaviour can be changed by setting the appropriate variable:
-```php
-\Longman\TelegramBot\TelegramLog::$remove_bot_token = false;
-```
-
-[PSR-3]: https://www.php-fig.org/psr/psr-3
-[PSR-3-providers]: https://packagist.org/providers/psr/log-implementation
-[Monolog]: https://github.com/Seldaek/monolog
diff --git a/examples/.env.example b/examples/.env.example
new file mode 100644
index 000000000..068ec4ccf
--- /dev/null
+++ b/examples/.env.example
@@ -0,0 +1,2 @@
+BOT_TOKEN=
+RECIPIENT_CHAT_ID=
\ No newline at end of file
diff --git a/examples/.gitignore b/examples/.gitignore
new file mode 100644
index 000000000..4cb512ec1
--- /dev/null
+++ b/examples/.gitignore
@@ -0,0 +1 @@
+/.env
\ No newline at end of file
diff --git a/examples/bootstrap.php b/examples/bootstrap.php
new file mode 100644
index 000000000..238434140
--- /dev/null
+++ b/examples/bootstrap.php
@@ -0,0 +1,7 @@
+<?php
+
+use Dotenv\Dotenv;
+
+require __DIR__ . '/../vendor/autoload.php';
+
+Dotenv::createImmutable(__DIR__)->safeLoad();
diff --git a/examples/files/example_photo.jpeg b/examples/files/example_photo.jpeg
new file mode 100644
index 000000000..b2efc26a6
Binary files /dev/null and b/examples/files/example_photo.jpeg differ
diff --git a/examples/logs/.gitignore b/examples/logs/.gitignore
new file mode 100644
index 000000000..c96a04f00
--- /dev/null
+++ b/examples/logs/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
\ No newline at end of file
diff --git a/examples/run.php b/examples/run.php
new file mode 100644
index 000000000..1ee636dae
--- /dev/null
+++ b/examples/run.php
@@ -0,0 +1,48 @@
+<?php
+
+use PhpTelegramBot\Core\Entities\Update;
+use PhpTelegramBot\Core\Events\IncomingUpdate;
+use PhpTelegramBot\Core\Telegram;
+
+require __DIR__ . '/bootstrap.php';
+
+$bot = new Telegram($_SERVER['BOT_TOKEN']);
+
+function save_dump(Update $update, Telegram $bot)
+{
+    $filename = __DIR__ . '/logs/' . uniqid(date('Y-m-d_H-i-s')) . '_update.json';
+    file_put_contents($filename, json_encode($update, JSON_PRETTY_PRINT));
+}
+
+$bot->registerEventListener(IncomingUpdate::class, function (IncomingUpdate $event, Telegram $bot) {
+    // Gets called on every incoming `Update` no matter what type
+    dump('IncomingUpdate');
+});
+
+$bot->registerUpdateTypes([
+    'edited_message' => function (Update $update, Telegram $bot) {
+        // Gets called on every `Update` of type "edited_message"
+        dump('edited_message');
+    },
+]);
+
+$bot->registerMessageTypes([
+    'text' => [
+        function (Update $update, Telegram $bot) {
+            // Gets called on every `Update` with a `Message` of type "text"
+            dump('message.text');
+            dump($update->getMessage()->getText());
+        },
+        'save_dump',
+    ],
+
+    'voice' => function (Update $update, Telegram $bot) {
+        // Gets called on every `Update` with a `Message` of type "voice".
+        dump('message.voice');
+        dump($update->getMessage()->getVoice());
+    },
+]);
+
+// Start 30s long polling in an infinite loop
+// See https://en.wikipedia.org/wiki/Push_technology#Long_polling
+$bot->handleGetUpdates();
diff --git a/examples/sendPhoto.php b/examples/sendPhoto.php
new file mode 100644
index 000000000..1465f1328
--- /dev/null
+++ b/examples/sendPhoto.php
@@ -0,0 +1,38 @@
+<?php
+
+require 'bootstrap.php';
+
+$bot = new \PhpTelegramBot\Core\Telegram($_SERVER['BOT_TOKEN']);
+$recipient = $_SERVER['RECIPIENT_CHAT_ID'];
+$filepath = __DIR__ . '/files/example_photo.jpeg';
+
+try {
+
+    $bot->sendMediaGroup([
+        'chat_id' => $recipient,
+        'media'   => [
+            [
+                'type'    => 'photo',
+                'media'   => $filepath,
+                'caption' => 'Photo #1',
+            ],
+            [
+                'type'    => 'photo',
+                'media'   => fopen($filepath, 'r'),
+                'caption' => 'Photo #2',
+            ],
+            [
+                'type'    => 'photo',
+                'media'   => new \GuzzleHttp\Psr7\Stream(fopen($filepath, 'r')),
+                'caption' => 'Photo #3',
+            ],
+        ],
+    ]);
+
+    echo 'Photos sent.';
+
+} catch (\PhpTelegramBot\Core\Exceptions\TelegramException $e) {
+
+    echo 'ERROR: ' . $e->getMessage();
+
+}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
deleted file mode 100644
index 7ba753144..000000000
--- a/phpcs.xml.dist
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="./vendor/squizlabs/php_codesniffer/phpcs.xsd">
-    <description>PHP Code Sniffer</description>
-
-    <arg value="snp"/>
-    <arg name="colors"/>
-    <arg name="extensions" value="php"/>
-    <arg name="encoding" value="utf-8"/>
-    <arg name="parallel" value="8"/>
-    <arg name="report-width" value="150"/>
-
-    <file>src/</file>
-    <file>tests/</file>
-    <file>utils/</file>
-
-    <rule ref="PSR12">
-        <exclude name="PSR12.Classes.OpeningBraceSpace.Found"/>
-    </rule>
-</ruleset>
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
deleted file mode 100644
index 036786b84..000000000
--- a/phpunit.xml.dist
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="./vendor/phpunit/phpunit/phpunit.xsd">
-    <coverage>
-        <include>
-            <directory suffix=".php">./src</directory>
-        </include>
-        <exclude>
-            <directory suffix=".php">./src/Exception</directory>
-        </exclude>
-    </coverage>
-    <php>
-        <ini name="date.timezone" value="UTC"/>
-        <ini name="error_reporting" value="-1"/>
-        <const name="PHPUNIT_TESTSUITE" value="true"/>
-        <const name="PHPUNIT_DB_HOST" value="127.0.0.1"/>
-        <const name="PHPUNIT_DB_PORT" value="3306"/>
-        <const name="PHPUNIT_DB_NAME" value="telegrambot"/>
-        <const name="PHPUNIT_DB_USER" value="root"/>
-        <const name="PHPUNIT_DB_PASS" value="root"/>
-    </php>
-    <testsuites>
-        <testsuite name="Package Test Suite">
-            <directory>./tests/</directory>
-        </testsuite>
-    </testsuites>
-</phpunit>
diff --git a/pint.json b/pint.json
new file mode 100644
index 000000000..e5818b4e4
--- /dev/null
+++ b/pint.json
@@ -0,0 +1,14 @@
+{
+  "preset": "laravel",
+  "rules": {
+    "phpdoc_align": true,
+    "binary_operator_spaces": {
+      "operators": {
+        "=>": "align"
+      }
+    },
+    "concat_space": {
+      "spacing": "one"
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/ApiMethods/AnswersInlineQueries.php b/src/ApiMethods/AnswersInlineQueries.php
new file mode 100644
index 000000000..4c56308b4
--- /dev/null
+++ b/src/ApiMethods/AnswersInlineQueries.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace PhpTelegramBot\Core\ApiMethods;
+
+use PhpTelegramBot\Core\Entities\InlineQueryResult\InlineQueryResult;
+use PhpTelegramBot\Core\Entities\InlineQueryResultsButton;
+use PhpTelegramBot\Core\Entities\SentWebAppMessage;
+
+trait AnswersInlineQueries
+{
+    /**
+     * @param array{
+     *     inline_query_id: string,
+     *     results: InlineQueryResult[],
+     *     cache_time: int,
+     *     is_personal: bool,
+     *     next_offset: string,
+     *     button: InlineQueryResultsButton,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function answerInlineQuery(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     web_app_query_id: string,
+     *     result: InlineQueryResult,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function answerWebAppQuery(array $data = []): SentWebAppMessage
+    {
+        return $this->send(__FUNCTION__, $data, SentWebAppMessage::class);
+    }
+}
diff --git a/src/ApiMethods/SendsInvoices.php b/src/ApiMethods/SendsInvoices.php
new file mode 100644
index 000000000..4f2045c2c
--- /dev/null
+++ b/src/ApiMethods/SendsInvoices.php
@@ -0,0 +1,138 @@
+<?php
+
+namespace PhpTelegramBot\Core\ApiMethods;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\LabeledPrice;
+use PhpTelegramBot\Core\Entities\Message;
+use PhpTelegramBot\Core\Entities\ReplyParameters;
+use PhpTelegramBot\Core\Entities\ShippingOption;
+use PhpTelegramBot\Core\Entities\StarTransactions;
+
+trait SendsInvoices
+{
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     title: string,
+     *     description: string,
+     *     payload: string,
+     *     provider_token: string,
+     *     currency: string,
+     *     prices: LabeledPrice[],
+     *     max_tip_amount: int,
+     *     suggested_tip_amounts: int[],
+     *     start_parameter: string,
+     *     provider_data: string,
+     *     photo_url: string,
+     *     photo_size: int,
+     *     photo_width: int,
+     *     photo_height: int,
+     *     need_name: bool,
+     *     need_phone_number: bool,
+     *     need_email: bool,
+     *     need_shipping_address: bool,
+     *     send_phone_number_to_provider: bool,
+     *     send_email_to_provider: bool,
+     *     is_flexible: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function sendInvoice(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     title: string,
+     *     description: string,
+     *     payload: string,
+     *     provider_token: string,
+     *     currency: string,
+     *     prices: LabeledPrice[],
+     *     max_tip_amount: int,
+     *     suggested_tip_amounts: int[],
+     *     provider_data: string,
+     *     photo_url: string,
+     *     photo_size: int,
+     *     photo_width: int,
+     *     photo_height: int,
+     *     need_name: bool,
+     *     need_phone_number: bool,
+     *     need_email: bool,
+     *     need_shipping_address: bool,
+     *     send_phone_number_to_provider: bool,
+     *     send_email_to_provider: bool,
+     *     is_flexible: bool,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function createInvoiceLink(array $data = []): string
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     shipping_query_id: string,
+     *     ok: bool,
+     *     shipping_options: ShippingOption[],
+     *     error_message: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function answerShippingQuery(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     pre_checkout_query_id: string,
+     *     ok: bool,
+     *     error_message: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function answerPreCheckoutQuery(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     offset: int,
+     *     limit: int,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function getStarTransactions(array $data = []): StarTransactions
+    {
+        return $this->send(__FUNCTION__, $data, StarTransactions::class);
+    }
+
+    /**
+     * @param array{
+     *     user_id: int,
+     *     telegram_payment_charge_id: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function refundStarPayment(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+}
diff --git a/src/ApiMethods/SendsMessages.php b/src/ApiMethods/SendsMessages.php
new file mode 100644
index 000000000..9e863de0e
--- /dev/null
+++ b/src/ApiMethods/SendsMessages.php
@@ -0,0 +1,1387 @@
+<?php
+
+namespace PhpTelegramBot\Core\ApiMethods;
+
+use PhpTelegramBot\Core\Entities\BotCommand;
+use PhpTelegramBot\Core\Entities\BotCommandScope\BotCommandScope;
+use PhpTelegramBot\Core\Entities\BotDescription;
+use PhpTelegramBot\Core\Entities\BotName;
+use PhpTelegramBot\Core\Entities\BotShortDescription;
+use PhpTelegramBot\Core\Entities\BusinessConnection;
+use PhpTelegramBot\Core\Entities\ChatAdministratorRights;
+use PhpTelegramBot\Core\Entities\ChatFullInfo;
+use PhpTelegramBot\Core\Entities\ChatInviteLink;
+use PhpTelegramBot\Core\Entities\ChatMember\ChatMember;
+use PhpTelegramBot\Core\Entities\ChatPermissions;
+use PhpTelegramBot\Core\Entities\File;
+use PhpTelegramBot\Core\Entities\ForceReply;
+use PhpTelegramBot\Core\Entities\ForumTopic;
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputFile;
+use PhpTelegramBot\Core\Entities\InputMedia\InputMedia;
+use PhpTelegramBot\Core\Entities\LinkPreviewOptions;
+use PhpTelegramBot\Core\Entities\MenuButton\MenuButton;
+use PhpTelegramBot\Core\Entities\Message;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+use PhpTelegramBot\Core\Entities\MessageId;
+use PhpTelegramBot\Core\Entities\ReactionType\ReactionType;
+use PhpTelegramBot\Core\Entities\ReplyKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\ReplyKeyboardRemove;
+use PhpTelegramBot\Core\Entities\ReplyParameters;
+use PhpTelegramBot\Core\Entities\Sticker;
+use PhpTelegramBot\Core\Entities\Update;
+use PhpTelegramBot\Core\Entities\User;
+use PhpTelegramBot\Core\Entities\UserChatBoosts;
+use PhpTelegramBot\Core\Entities\UserProfilePhotos;
+use PhpTelegramBot\Core\Entities\WebhookInfo;
+use PhpTelegramBot\Core\Exceptions;
+
+trait SendsMessages
+{
+    /**
+     * @param  array{
+     *     offset: int,
+     *     limit: int,
+     *     timeout: int,
+     *     allowed_updates: array<string>
+     * } $data
+     * @return array<Update>
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getUpdates(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [Update::class]);
+    }
+
+    /**
+     * @param array{
+     *     url: string,
+     *     certificate: InputFile,
+     *     ip_address: string,
+     *     max_connections: int,
+     *     allowed_updates: array<string>,
+     *     drop_pending_updates: bool,
+     *     secret_token: string
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setWebhook(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data);
+    }
+
+    /**
+     * @param array{
+     *     drop_pending_updates: bool
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function deleteWebhook(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data);
+    }
+
+    /**
+     * @throws Exceptions\TelegramException
+     */
+    public function getWebhookInfo(array $data = []): WebhookInfo
+    {
+        return $this->send(__FUNCTION__, $data, WebhookInfo::class);
+    }
+
+    /**
+     * @throws Exceptions\TelegramException
+     */
+    public function getMe(array $data = []): User
+    {
+        return $this->send(__FUNCTION__, $data, User::class);
+    }
+
+    public function logOut(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data);
+    }
+
+    /**
+     * @throws Exceptions\TelegramException
+     */
+    public function close(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     text: string,
+     *     parse_mode: string,
+     *     entities: MessageEntity[],
+     *     link_preview_options: LinkPreviewOptions,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendMessage(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     from_chat_id: int|string,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function forwardMessage(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     from_chat_id: int|string,
+     *     message_ids: int[],
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     * } $data
+     * @return MessageId[]
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function forwardMessages(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [MessageId::class]);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     from_chat_id: int|string,
+     *     message_id: int,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     show_caption_above_media: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function copyMessage(array $data = []): MessageId
+    {
+        return $this->send(__FUNCTION__, $data, MessageId::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     from_chat_id: int|string,
+     *     message_ids: int[],
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     remove_caption: bool,
+     * } $data
+     * @return MessageId[]
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function copyMessages(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [MessageId::class]);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     photo: string|InputFile,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     show_caption_above_media: bool,
+     *     has_spoiler: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendPhoto(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     audio: string|InputFile,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     duration: int,
+     *     performer: string,
+     *     title: string,
+     *     thumbnail: string|InputFile,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendAudio(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     document: string|InputFile,
+     *     thumbnail: string|InputFile,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     disable_content_type_detection: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendDocument(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     video: string|InputFile,
+     *     duration: int,
+     *     width: int,
+     *     height: int,
+     *     thumbnail: string|InputFile,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     show_caption_above_media: bool,
+     *     has_spoiler: bool,
+     *     supports_streaming: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendVideo(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     animation: string|InputFile,
+     *     duration: int,
+     *     width: int,
+     *     height: int,
+     *     thumbnail: string|InputFile,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     show_caption_above_media: bool,
+     *     has_spoiler: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendAnimation(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     voice: string|InputFile,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     duration: int,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendVoice(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     video_note: string|InputFile,
+     *     duration: int,
+     *     length: int,
+     *     thumbnail: string|InputFile,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendVideoNote(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     star_count: int,
+     *     media: InputPaidMedia[],
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     show_caption_above_media: bool,
+     *     disable_notification: bool,
+     *     parse_content: bool,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function sendPaidMedia(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     media: InputMedia[],
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     * } $data
+     * @return Message[]
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendMediaGroup(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [Message::class]);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     latitude: float,
+     *     longitude: float,
+     *     horizontal_accuracy: float,
+     *     live_period: int,
+     *     heading: int,
+     *     proximity_alert_radius: int,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendLocation(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     latitude: float,
+     *     longitude: float,
+     *     title: string,
+     *     address: string,
+     *     foursquare_id: string,
+     *     foursquare_type: string,
+     *     google_place_id: string,
+     *     google_place_type: string,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendVenue(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     phone_number: string,
+     *     first_name: string,
+     *     last_name: string,
+     *     vcard: string,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendContact(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     question: string,
+     *     question_parse_mode: string,
+     *     question_entities: array,
+     *     options: array,
+     *     is_anonymous: bool,
+     *     type: string,
+     *     allows_multiple_answers: bool,
+     *     correct_option_id: int,
+     *     explanation: string,
+     *     explanation_parse_mode: string,
+     *     explanation_entities: array,
+     *     open_period: int,
+     *     close_date: int,
+     *     is_closed: bool,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendPoll(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     emoji: string,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendDice(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     action: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendChatAction(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     reaction: ReactionType[],
+     *     is_big: bool
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function sendMessageReaction(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     user_id: int,
+     *     offset: int,
+     *     limit: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getUserProfilePhotos(array $data = []): UserProfilePhotos
+    {
+        return $this->send(__FUNCTION__, $data, UserProfilePhotos::class);
+    }
+
+    /**
+     * @param array{
+     *     file_id: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getFile(array $data = []): File
+    {
+        return $this->send(__FUNCTION__, $data, File::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     *     until_date: int,
+     *     revoke_messages: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function banChatMember(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     *     only_if_banned: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unbanChatMember(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     *     permissions: ChatPermissions,
+     *     use_independent_chat_permissions: bool,
+     *     until_date: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function restrictChatMember(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     *     is_anonymous: bool,
+     *     can_manage_chat: bool,
+     *     can_delete_messages: bool,
+     *     can_manage_video_chats: bool,
+     *     can_restrict_members: bool,
+     *     can_promote_members: bool,
+     *     can_change_info: bool,
+     *     can_invite_users: bool,
+     *     can_post_stories: bool,
+     *     can_edit_stories: bool,
+     *     can_delete_stories: bool,
+     *     can_post_messages: bool,
+     *     can_edit_messages: bool,
+     *     can_pin_messages: bool,
+     *     can_manage_topics: bool
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function promoteChatMember(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     *     custom_title: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatAdministratorCustomTitle(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     sender_chat_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function banChatSenderChat(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     sender_chat_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unbanChatSenderChat(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     permissions: ChatPermissions,
+     *     use_independent_chat_permissions: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatPermissions(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function exportChatInviteLink(array $data = []): string
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     name: string,
+     *     expire_date: int,
+     *     member_limit: int,
+     *     creates_join_request: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function createChatInviteLink(array $data = []): ChatInviteLink
+    {
+        return $this->send(__FUNCTION__, $data, ChatInviteLink::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     invite_link: string,
+     *     name: string,
+     *     expire_date: int,
+     *     member_limit: int,
+     *     creates_join_request: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function editChatInviteLink(array $data = []): ChatInviteLink
+    {
+        return $this->send(__FUNCTION__, $data, ChatInviteLink::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     invite_link: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function revokeChatInviteLink(array $data = []): ChatInviteLink
+    {
+        return $this->send(__FUNCTION__, $data, ChatInviteLink::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function approveChatJoinRequest(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function declineChatJoinRequest(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     photo: InputFile,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatPhoto(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function deleteChatPhoto(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     title: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatTitle(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     description: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatDescription(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     disable_notification: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function pinChatMessage(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unpinChatMessage(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unpinAllChatMessages(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function leaveChat(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getChat(array $data = []): ChatFullInfo
+    {
+        return $this->send(__FUNCTION__, $data, ChatFullInfo::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     * @return ChatMember[]
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getChatAdministrator(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [ChatMember::class]);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getChatMemberCount(array $data = []): int
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getChatMember(array $data = []): ChatMember
+    {
+        return $this->send(__FUNCTION__, $data, ChatMember::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     sticker_set_name: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatStickerSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function deleteChatStickerSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *
+     * } $data
+     * @return Sticker[]
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getForumTopicIconStickers(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [Sticker::class]);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     name: string,
+     *     icon_color: int,
+     *     icon_custom_emoji_id: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function createForumTopic(array $data = []): ForumTopic
+    {
+        return $this->send(__FUNCTION__, $data, ForumTopic::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     name: string,
+     *     icon_custom_emoji_id: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function editForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function closeForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function reopenForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function deleteForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unpinAllForumTopicMessages(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     name: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function editGeneralForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function closeGeneralForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function reopenGeneralForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function hideGeneralForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unhideGeneralForumTopic(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function unpinAllGeneralForumTopicMessages(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     callback_query_id: string,
+     *     text: string,
+     *     show_alert: bool,
+     *     url: string,
+     *     cache_time: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function answerCallbackQuery(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     user_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getUserChatBoosts(array $data = []): UserChatBoosts
+    {
+        return $this->send(__FUNCTION__, $data, UserChatBoosts::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getBusinessConnection(array $data = []): BusinessConnection
+    {
+        return $this->send(__FUNCTION__, $data, BusinessConnection::class);
+    }
+
+    /**
+     * @param array{
+     *     commands: BotCommand[],
+     *     scope: BotCommandScope,
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setMyCommands(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     scope: BotCommandScope,
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function deleteMyCommands(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     scope: BotCommandScope,
+     *     language_code: string,
+     * } $data
+     * @return BotCommand[]
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getMyCommands(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [BotCommand::class]);
+    }
+
+    /**
+     * @param array{
+     *     name: string,
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setMyName(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getMyName(array $data = []): BotName
+    {
+        return $this->send(__FUNCTION__, $data, BotName::class);
+    }
+
+    /**
+     * @param array{
+     *     description: string,
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setMyDescription(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getMyDescription(array $data = []): BotDescription
+    {
+        return $this->send(__FUNCTION__, $data, BotDescription::class);
+    }
+
+    /**
+     * @param array{
+     *     short_description: string,
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setMyShortDescription(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     language_code: string,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getMyShortDescription(array $data = []): BotShortDescription
+    {
+        return $this->send(__FUNCTION__, $data, BotShortDescription::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int,
+     *     menu_button: MenuButton,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setChatMenuButton(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getChatMenuButton(array $data = []): MenuButton
+    {
+        return $this->send(__FUNCTION__, $data, MenuButton::class);
+    }
+
+    /**
+     * @param array{
+     *     rights: ChatAdministratorRights,
+     *     for_channels: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function setMyDefaultAdministratorRights(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     for_channels: bool,
+     * } $data
+     *
+     * @throws Exceptions\TelegramException
+     */
+    public function getMyDefaultAdministratorRights(array $data = []): ChatAdministratorRights
+    {
+        return $this->send(__FUNCTION__, $data, ChatAdministratorRights::class);
+    }
+}
diff --git a/src/ApiMethods/SendsStickers.php b/src/ApiMethods/SendsStickers.php
new file mode 100644
index 000000000..0bfd890f0
--- /dev/null
+++ b/src/ApiMethods/SendsStickers.php
@@ -0,0 +1,242 @@
+<?php
+
+namespace PhpTelegramBot\Core\ApiMethods;
+
+use PhpTelegramBot\Core\Entities\File;
+use PhpTelegramBot\Core\Entities\ForceReply;
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputFile;
+use PhpTelegramBot\Core\Entities\InputSticker;
+use PhpTelegramBot\Core\Entities\MaskPosition;
+use PhpTelegramBot\Core\Entities\Message;
+use PhpTelegramBot\Core\Entities\ReplyKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\ReplyKeyboardRemove;
+use PhpTelegramBot\Core\Entities\ReplyParameters;
+use PhpTelegramBot\Core\Entities\Sticker;
+use PhpTelegramBot\Core\Entities\StickerSet;
+
+trait SendsStickers
+{
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_thread_id: int,
+     *     sticker: InputFile|string,
+     *     emoji: string,
+     *     disable_notification: bool,
+     *     protect_content: bool,
+     *     message_effect_id: string,
+     *     reply_parameters: ReplyParameters,
+     *     reply_markup: InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function sendSticker(array $data = []): Message
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     name: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function getStickerSet(array $data = []): StickerSet
+    {
+        return $this->send(__FUNCTION__, $data, StickerSet::class);
+    }
+
+    /**
+     * @param array{
+     *     custom_emoji_ids: string[],
+     * } $data
+     * @return Sticker[]
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function getCustomEmojiStickers(array $data = []): array
+    {
+        return $this->send(__FUNCTION__, $data, [Sticker::class]);
+    }
+
+    /**
+     * @param array{
+     *     user_id: int,
+     *     sticker: InputFile,
+     *     sticker_format: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function uploadStickerFile(array $data = []): File
+    {
+        return $this->send(__FUNCTION__, $data, File::class);
+    }
+
+    /**
+     * @param array{
+     *     user_id: int,
+     *     name: string,
+     *     title: string,
+     *     stickers: InputSticker[],
+     *     sticker_type: string,
+     *     needs_repainting: bool,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function createNewStickerSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     user_id: int,
+     *     name: string,
+     *     sticker: InputSticker,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function addStickerToSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     sticker: string,
+     *     position: int,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setStickerPositionInSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     sticker: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function deleteStickerFromSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     user_id: int,
+     *     name: string,
+     *     old_sticker: string,
+     *     sticker: InputSticker,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function replaceStickerInSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     sticker: string,
+     *     emoji_list: string[],
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setStickerEmojiList(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     sticker: string,
+     *     keywords: string[],
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setStickerKeywords(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     sticker: string,
+     *     mask_position: MaskPosition,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setStickerMaskPosition(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     name: string,
+     *     title: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setStickerSetTitle(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     name: string,
+     *     user_id: int,
+     *     thumbnail: InputFile|string,
+     *     format: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setStickerSetThumbnail(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     name: string,
+     *     custom_emoji_id: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function setCustomEmojiStickerSetThumbnail(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     name: string,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function deleteStickerSet(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+}
diff --git a/src/ApiMethods/UpdatesMessages.php b/src/ApiMethods/UpdatesMessages.php
new file mode 100644
index 000000000..771b3439c
--- /dev/null
+++ b/src/ApiMethods/UpdatesMessages.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace PhpTelegramBot\Core\ApiMethods;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMedia\InputMedia;
+use PhpTelegramBot\Core\Entities\LinkPreviewOptions;
+use PhpTelegramBot\Core\Entities\Message;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+use PhpTelegramBot\Core\Entities\Poll;
+use PhpTelegramBot\Core\Exceptions\TelegramException;
+
+trait UpdatesMessages
+{
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     inline_message_id: string,
+     *     text: string,
+     *     parse_mode: string,
+     *     entities: MessageEntity[],
+     *     link_preview_options: LinkPreviewOptions,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws TelegramException
+     */
+    public function editMessageText(array $data = []): Message|bool
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     inline_message_id: string,
+     *     caption: string,
+     *     parse_mode: string,
+     *     caption_entities: MessageEntity[],
+     *     show_caption_above_media:bool,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws TelegramException
+     */
+    public function editMessageCaption(array $data = []): Message|bool
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     inline_message_id: string,
+     *     media: InputMedia,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function editMessageMedia(array $data = []): Message|bool
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     inline_message_id: string,
+     *     latitude: float,
+     *     longitude: float,
+     *     live_period: int,
+     *     horizontal_accuracy: float,
+     *     heading: int,
+     *     proximity_alert_radius: int,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function editMessageLiveLocation(array $data = []): Message|bool
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     inline_message_id: string,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function stopMessageLiveLocation(array $data = []): Message|bool
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     businss_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     inline_message_id: string,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function editMessageReplyMarkup(array $data = []): Message|bool
+    {
+        return $this->send(__FUNCTION__, $data, Message::class);
+    }
+
+    /**
+     * @param array{
+     *     business_connection_id: string,
+     *     chat_id: int|string,
+     *     message_id: int,
+     *     reply_markup: InlineKeyboardMarkup,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function stopPoll(array $data = []): Poll
+    {
+        return $this->send(__FUNCTION__, $data, Poll::class);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_id: int,
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function deleteMessage(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+
+    /**
+     * @param array{
+     *     chat_id: int|string,
+     *     message_ids: int[],
+     * } $data
+     *
+     * @throws \PhpTelegramBot\Core\Exceptions\TelegramException
+     */
+    public function deleteMessages(array $data = []): bool
+    {
+        return $this->send(__FUNCTION__, $data, null);
+    }
+}
diff --git a/src/ChatAction.php b/src/ChatAction.php
deleted file mode 100644
index c98f424e1..000000000
--- a/src/ChatAction.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot;
-
-class ChatAction
-{
-    /**
-     * Typing chat action
-     */
-    public const TYPING = 'typing';
-
-    /**
-     * Upload Photo chat action
-     */
-    public const UPLOAD_PHOTO = 'upload_photo';
-
-    /**
-     * Record Video chat action
-     */
-    public const RECORD_VIDEO = 'record_video';
-
-    /**
-     * Upload Video chat action
-     */
-    public const UPLOAD_VIDEO = 'upload_video';
-
-    /**
-     * Record Voice chat action
-     */
-    public const RECORD_VOICE = 'record_voice';
-
-    /**
-     * Upload Voice chat action
-     */
-    public const UPLOAD_VOICE = 'upload_voice';
-
-    /**
-     * Upload Document chat action
-     */
-    public const UPLOAD_DOCUMENT = 'upload_document';
-
-    /**
-     * Choose Sticker chat action
-     */
-    public const CHOOSE_STICKER = 'choose_sticker';
-
-    /**
-     * Find Location chat action
-     */
-    public const FIND_LOCATION = 'find_location';
-
-    /**
-     * Record Video Note chat action
-     */
-    public const RECORD_VIDEO_NOTE = 'record_video_note';
-
-    /**
-     * Upload Video note chat action
-     */
-    public const UPLOAD_VIDEO_NOTE = 'upload_video_note';
-}
diff --git a/src/Commands/AdminCommand.php b/src/Commands/AdminCommand.php
deleted file mode 100644
index d63f6b60c..000000000
--- a/src/Commands/AdminCommand.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands;
-
-abstract class AdminCommand extends Command
-{
-    /**
-     * @var bool
-     */
-    protected $private_only = true;
-}
diff --git a/src/Commands/AdminCommands/ChatsCommand.php b/src/Commands/AdminCommands/ChatsCommand.php
deleted file mode 100644
index d3d818a54..000000000
--- a/src/Commands/AdminCommands/ChatsCommand.php
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\AdminCommands;
-
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-
-class ChatsCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'chats';
-
-    /**
-     * @var string
-     */
-    protected $description = 'List or search all chats stored by the bot';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/chats, /chats * or /chats <search string>';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.2.0';
-
-    /**
-     * @var bool
-     */
-    protected $need_mysql = true;
-
-    /**
-     * Command execute method
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-
-        $chat_id = $message->getChat()->getId();
-        $text    = trim($message->getText(true));
-
-        $results = DB::selectChats([
-            'groups'      => true,
-            'supergroups' => true,
-            'channels'    => true,
-            'users'       => true,
-            'text'        => ($text === '' || $text === '*') ? null : $text //Text to search in user/group name
-        ]);
-
-        $user_chats       = 0;
-        $group_chats      = 0;
-        $supergroup_chats = 0;
-        $channel_chats    = 0;
-
-        if ($text === '') {
-            $text_back = '';
-        } elseif ($text === '*') {
-            $text_back = 'List of all bot chats:' . PHP_EOL;
-        } else {
-            $text_back = 'Chat search results:' . PHP_EOL;
-        }
-
-        if (is_array($results)) {
-            foreach ($results as $result) {
-                //Initialize a chat object
-                $result['id'] = $result['chat_id'];
-                $chat         = new Chat($result);
-
-                $whois = $chat->getId();
-                if ($this->telegram->getCommandObject('whois')) {
-                    // We can't use '-' in command because part of it will become unclickable
-                    $whois = '/whois' . str_replace('-', 'g', $chat->getId());
-                }
-
-                if ($chat->isPrivateChat()) {
-                    if ($text !== '') {
-                        $text_back .= '- P ' . $chat->tryMention() . ' [' . $whois . ']' . PHP_EOL;
-                    }
-
-                    ++$user_chats;
-                } elseif ($chat->isSuperGroup()) {
-                    if ($text !== '') {
-                        $text_back .= '- S ' . $chat->getTitle() . ' [' . $whois . ']' . PHP_EOL;
-                    }
-
-                    ++$supergroup_chats;
-                } elseif ($chat->isGroupChat()) {
-                    if ($text !== '') {
-                        $text_back .= '- G ' . $chat->getTitle() . ' [' . $whois . ']' . PHP_EOL;
-                    }
-
-                    ++$group_chats;
-                } elseif ($chat->isChannel()) {
-                    if ($text !== '') {
-                        $text_back .= '- C ' . $chat->getTitle() . ' [' . $whois . ']' . PHP_EOL;
-                    }
-
-                    ++$channel_chats;
-                }
-            }
-        }
-
-        if (($user_chats + $group_chats + $supergroup_chats) === 0) {
-            $text_back = 'No chats found..';
-        } else {
-            $text_back .= PHP_EOL . 'Private Chats: ' . $user_chats;
-            $text_back .= PHP_EOL . 'Groups: ' . $group_chats;
-            $text_back .= PHP_EOL . 'Super Groups: ' . $supergroup_chats;
-            $text_back .= PHP_EOL . 'Channels: ' . $channel_chats;
-            $text_back .= PHP_EOL . 'Total: ' . ($user_chats + $group_chats + $supergroup_chats);
-
-            if ($text === '') {
-                $text_back .= PHP_EOL . PHP_EOL . 'List all chats: /' . $this->name . ' *' . PHP_EOL . 'Search for chats: /' . $this->name . ' <search string>';
-            }
-        }
-
-        $data = [
-            'chat_id' => $chat_id,
-            'text'    => $text_back,
-        ];
-
-        return Request::sendMessage($data);
-    }
-}
diff --git a/src/Commands/AdminCommands/CleanupCommand.php b/src/Commands/AdminCommands/CleanupCommand.php
deleted file mode 100644
index 221ed932d..000000000
--- a/src/Commands/AdminCommands/CleanupCommand.php
+++ /dev/null
@@ -1,435 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\AdminCommands;
-
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-use Longman\TelegramBot\TelegramLog;
-use PDOException;
-
-/**
- * User "/cleanup" command
- *
- * Configuration options:
- *
- * $telegram->setCommandConfig('cleanup', [
- *     // Define which tables should be cleaned.
- *     'tables_to_clean' => [
- *         'message',
- *         'edited_message',
- *     ],
- *     // Define how old cleaned entries should be.
- *     'clean_older_than' => [
- *         'message'        => '7 days',
- *         'edited_message' => '30 days',
- *     ]
- * );
- */
-class CleanupCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'cleanup';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Clean up the database from old records';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/cleanup [dry] <days> or /cleanup [dry] <count> <unit> (e.g. 3 weeks)';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.1.0';
-
-    /**
-     * @var bool
-     */
-    protected $need_mysql = true;
-
-    /**
-     * Default tables to clean, cleaning 'chat', 'user' and 'user_chat' by default is bad practice!
-     *
-     * @var array
-     */
-    protected static $default_tables_to_clean = [
-        'callback_query',
-        'chosen_inline_result',
-        'conversation',
-        'edited_message',
-        'inline_query',
-        'message',
-        'request_limiter',
-        'telegram_update',
-    ];
-
-    /**
-     * By default, remove records older than X days/hours/anything from these tables.
-     *
-     * @var array
-     */
-    protected static $default_clean_older_than = [
-        'callback_query'       => '30 days',
-        'chat'                 => '365 days',
-        'chosen_inline_result' => '30 days',
-        'conversation'         => '90 days',
-        'edited_message'       => '30 days',
-        'inline_query'         => '30 days',
-        'message'              => '30 days',
-        'poll'                 => '90 days',
-        'request_limiter'      => '1 minute',
-        'shipping_query'       => '90 days',
-        'telegram_update'      => '30 days',
-        'user'                 => '365 days',
-        'user_chat'            => '365 days',
-    ];
-
-    /**
-     * Set command config
-     *
-     * @param string $custom_time
-     *
-     * @return array
-     */
-    private function getSettings($custom_time = ''): array
-    {
-        $tables_to_clean      = self::$default_tables_to_clean;
-        $user_tables_to_clean = $this->getConfig('tables_to_clean');
-        if (is_array($user_tables_to_clean)) {
-            $tables_to_clean = $user_tables_to_clean;
-        }
-
-        $clean_older_than      = self::$default_clean_older_than;
-        $user_clean_older_than = $this->getConfig('clean_older_than');
-        if (is_array($user_clean_older_than)) {
-            $clean_older_than = array_merge($clean_older_than, $user_clean_older_than);
-        }
-
-        // Convert numeric-only values to days.
-        array_walk($clean_older_than, function (&$time) use ($custom_time) {
-            if (!empty($custom_time)) {
-                $time = $custom_time;
-            }
-            if (is_numeric($time)) {
-                $time .= ' days';
-            }
-        });
-
-        return compact('tables_to_clean', 'clean_older_than');
-    }
-
-    /**
-     * Get SQL queries array based on settings provided
-     *
-     * @param $settings
-     *
-     * @return array
-     * @throws TelegramException
-     */
-    private function getQueries($settings): array
-    {
-        if (empty($settings) || !is_array($settings)) {
-            throw new TelegramException('Settings variable is not an array or is empty!');
-        }
-
-        // Convert all clean_older_than times to correct format.
-        $clean_older_than = $settings['clean_older_than'];
-        foreach ($clean_older_than as $table => $time) {
-            $clean_older_than[$table] = date('Y-m-d H:i:s', strtotime('-' . $time));
-        }
-        $tables_to_clean = $settings['tables_to_clean'];
-
-        $queries = [];
-
-        if (in_array('telegram_update', $tables_to_clean, true)) {
-            $queries[] = sprintf(
-                'DELETE FROM `%3$s`
-                WHERE id IN (
-                    SELECT id FROM (
-                        SELECT id FROM `%3$s`
-                        WHERE `id` != \'%1$s\'
-                        AND `chat_id` NOT IN (
-                          SELECT `id`
-                          FROM `%4$s`
-                          WHERE `%3$s`.`chat_id` = `id`
-                          AND `updated_at` < \'%2$s\'
-                        )
-                        AND (
-                          `message_id` IS NOT NULL
-                          AND `message_id` IN (
-                            SELECT `id`
-                            FROM `%5$s`
-                            WHERE `date` < \'%2$s\'
-                          )
-                        )
-                        OR (
-                          `edited_message_id` IS NOT NULL
-                          AND `edited_message_id` IN (
-                            SELECT `id`
-                            FROM `%6$s`
-                            WHERE `edit_date` < \'%2$s\'
-                          )
-                        )
-                        OR (
-                          `inline_query_id` IS NOT NULL
-                          AND `inline_query_id` IN (
-                            SELECT `id`
-                            FROM `%7$s`
-                            WHERE `created_at` < \'%2$s\'
-                          )
-                        )
-                        OR (
-                          `chosen_inline_result_id` IS NOT NULL
-                          AND `chosen_inline_result_id` IN (
-                            SELECT `id`
-                            FROM `%8$s`
-                            WHERE `created_at` < \'%2$s\'
-                          )
-                        )
-                        OR (
-                          `callback_query_id` IS NOT NULL
-                          AND `callback_query_id` IN (
-                            SELECT `id`
-                            FROM `%9$s`
-                            WHERE `created_at` < \'%2$s\'
-                          )
-                        )
-                    ) a
-                )
-            ',
-                $this->getUpdate()->getUpdateId(),
-                $clean_older_than['telegram_update'],
-                TB_TELEGRAM_UPDATE,
-                TB_CHAT,
-                TB_MESSAGE,
-                TB_EDITED_MESSAGE,
-                TB_INLINE_QUERY,
-                TB_CHOSEN_INLINE_RESULT,
-                TB_CALLBACK_QUERY
-            );
-        }
-
-        if (in_array('user_chat', $tables_to_clean, true)) {
-            $queries[] = sprintf(
-                'DELETE FROM `%1$s`
-                WHERE `user_id` IN (
-                  SELECT `id`
-                  FROM `%2$s`
-                  WHERE `updated_at` < \'%3$s\'
-                )
-            ',
-                TB_USER_CHAT,
-                TB_USER,
-                $clean_older_than['chat']
-            );
-        }
-
-        // Simple.
-        $simple_tables = [
-            'user'            => ['table' => TB_USER, 'field' => 'updated_at'],
-            'chat'            => ['table' => TB_CHAT, 'field' => 'updated_at'],
-            'conversation'    => ['table' => TB_CONVERSATION, 'field' => 'updated_at'],
-            'poll'            => ['table' => TB_POLL, 'field' => 'created_at'],
-            'request_limiter' => ['table' => TB_REQUEST_LIMITER, 'field' => 'created_at'],
-            'shipping_query'  => ['table' => TB_SHIPPING_QUERY, 'field' => 'created_at'],
-        ];
-
-        foreach (array_intersect(array_keys($simple_tables), $tables_to_clean) as $table_to_clean) {
-            $queries[] = sprintf(
-                'DELETE FROM `%1$s`
-                WHERE `%2$s` < \'%3$s\'
-            ',
-                $simple_tables[$table_to_clean]['table'],
-                $simple_tables[$table_to_clean]['field'],
-                $clean_older_than[$table_to_clean]
-            );
-        }
-
-        // Queries.
-        $query_tables = [
-            'inline_query'         => ['table' => TB_INLINE_QUERY, 'field' => 'created_at'],
-            'chosen_inline_result' => ['table' => TB_CHOSEN_INLINE_RESULT, 'field' => 'created_at'],
-            'callback_query'       => ['table' => TB_CALLBACK_QUERY, 'field' => 'created_at'],
-        ];
-        foreach (array_intersect(array_keys($query_tables), $tables_to_clean) as $table_to_clean) {
-            $queries[] = sprintf(
-                'DELETE FROM `%1$s`
-                WHERE `%2$s` < \'%3$s\'
-                  AND `id` NOT IN (
-                    SELECT `%4$s`
-                    FROM `%5$s`
-                    WHERE `%4$s` = `%1$s`.`id`
-                  )
-            ',
-                $query_tables[$table_to_clean]['table'],
-                $query_tables[$table_to_clean]['field'],
-                $clean_older_than[$table_to_clean],
-                $table_to_clean . '_id',
-                TB_TELEGRAM_UPDATE
-            );
-        }
-
-        // Messages
-        if (in_array('edited_message', $tables_to_clean, true)) {
-            $queries[] = sprintf(
-                'DELETE FROM `%1$s`
-                WHERE `edit_date` < \'%2$s\'
-                  AND `id` NOT IN (
-                    SELECT `message_id`
-                    FROM `%3$s`
-                    WHERE `edited_message_id` = `%1$s`.`id`
-                  )
-            ',
-                TB_EDITED_MESSAGE,
-                $clean_older_than['edited_message'],
-                TB_TELEGRAM_UPDATE
-            );
-        }
-
-        if (in_array('message', $tables_to_clean, true)) {
-            $queries[] = sprintf(
-                'DELETE FROM `%1$s`
-                WHERE id IN (
-                    SELECT id
-                    FROM (
-                        SELECT id
-                        FROM  `%1$s`
-                        WHERE `date` < \'%2$s\'
-                          AND `id` NOT IN (
-                            SELECT `message_id`
-                            FROM `%3$s`
-                            WHERE `message_id` = `%1$s`.`id`
-                          )
-                          AND `id` NOT IN (
-                            SELECT `message_id`
-                            FROM `%4$s`
-                            WHERE `message_id` = `%1$s`.`id`
-                          )
-                          AND `id` NOT IN (
-                            SELECT `message_id`
-                            FROM `%5$s`
-                            WHERE `message_id` = `%1$s`.`id`
-                          )
-                          AND `id` NOT IN (
-                            SELECT a.`reply_to_message` FROM `%1$s` a
-                            INNER JOIN `%1$s` b ON b.`id` = a.`reply_to_message` AND b.`chat_id` = a.`reply_to_chat`
-                          )
-                        ORDER BY `id` DESC
-                     ) a
-                 )
-            ',
-                TB_MESSAGE,
-                $clean_older_than['message'],
-                TB_EDITED_MESSAGE,
-                TB_TELEGRAM_UPDATE,
-                TB_CALLBACK_QUERY
-            );
-        }
-
-        return $queries;
-    }
-
-    /**
-     * Execution if MySQL is required but not available
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function executeNoDb(): ServerResponse
-    {
-        return $this->replyToChat('*No database connection!*', ['parse_mode' => 'Markdown']);
-    }
-
-    /**
-     * Command execute method
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        $text    = $message->getText(true);
-
-        // Dry run?
-        $dry_run = strpos($text, 'dry') !== false;
-        $text    = trim(str_replace('dry', '', $text));
-
-        $settings = $this->getSettings($text);
-        $queries  = $this->getQueries($settings);
-
-        if ($dry_run) {
-            return $this->replyToUser('Queries:' . PHP_EOL . implode(PHP_EOL, $queries));
-        }
-
-        $infos = [];
-        foreach ($settings['tables_to_clean'] as $table) {
-            $info = "*{$table}*";
-
-            if (isset($settings['clean_older_than'][$table])) {
-                $info .= " ({$settings['clean_older_than'][$table]})";
-            }
-
-            $infos[] = $info;
-        }
-
-        $data = [
-            'chat_id'    => $message->getFrom()->getId(),
-            'parse_mode' => 'Markdown',
-        ];
-
-        $data['text'] = 'Cleaning up tables:' . PHP_EOL . implode(PHP_EOL, $infos);
-        Request::sendMessage($data);
-
-        $rows = 0;
-        $pdo  = DB::getPdo();
-        try {
-            $pdo->beginTransaction();
-
-            foreach ($queries as $query) {
-                // Delete in chunks to not block / improve speed on big tables.
-                $query .= ' LIMIT 10000';
-                while ($dbq = $pdo->query($query)) {
-                    if ($dbq->rowCount() === 0) {
-                        continue 2;
-                    }
-                    $rows += $dbq->rowCount();
-                }
-
-                TelegramLog::error('Error while executing query: ' . $query);
-            }
-
-            // commit changes to the database and end transaction
-            $pdo->commit();
-
-            $data['text'] = "*Database cleanup done!* _(removed {$rows} rows)_";
-        } catch (PDOException $e) {
-            $data['text'] = '*Database cleanup failed!* _(check your error logs)_';
-
-            // rollback changes on exception
-            // useful if you want to track down error you can't replicate it when some of the data is already deleted
-            $pdo->rollBack();
-
-            TelegramLog::error($e->getMessage());
-        }
-
-        return Request::sendMessage($data);
-    }
-}
diff --git a/src/Commands/AdminCommands/DebugCommand.php b/src/Commands/AdminCommands/DebugCommand.php
deleted file mode 100644
index 8a2fe80e4..000000000
--- a/src/Commands/AdminCommands/DebugCommand.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\AdminCommands;
-
-use Exception;
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-
-/**
- * Admin "/debug" command
- */
-class DebugCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'debug';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Debug command to help find issues';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/debug';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.1.0';
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        $pdo     = DB::getPdo();
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        $chat    = $message->getChat();
-        $text    = strtolower($message->getText(true));
-
-        $data = ['chat_id' => $chat->getId()];
-
-        if ($text !== 'glasnost' && !$chat->isPrivateChat()) {
-            $data['text'] = 'Only available in a private chat.';
-
-            return Request::sendMessage($data);
-        }
-
-        $debug_info = [];
-
-        $debug_info[] = sprintf('*TelegramBot version:* `%s`', $this->telegram->getVersion());
-        $debug_info[] = sprintf('*Download path:* `%s`', $this->telegram->getDownloadPath() ?: '`_Not set_`');
-        $debug_info[] = sprintf('*Upload path:* `%s`', $this->telegram->getUploadPath() ?: '`_Not set_`');
-
-        // Commands paths.
-        $debug_info[] = '*Commands paths:*';
-        $debug_info[] = sprintf(
-            '```' . PHP_EOL . '%s```',
-            json_encode($this->telegram->getCommandsPaths(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)
-        );
-
-        $php_bit = '';
-        PHP_INT_SIZE === 4 && $php_bit = ' (32bit)';
-        PHP_INT_SIZE === 8 && $php_bit = ' (64bit)';
-        $debug_info[] = sprintf('*PHP version:* `%1$s%2$s; %3$s; %4$s`', PHP_VERSION, $php_bit, PHP_SAPI, PHP_OS);
-        $debug_info[] = sprintf('*Maximum PHP script execution time:* `%d seconds`', ini_get('max_execution_time'));
-
-        $mysql_version = $pdo ? $pdo->query('SELECT VERSION() AS version')->fetchColumn() : null;
-        $debug_info[]  = sprintf('*MySQL version:* `%s`', $mysql_version ?: 'disabled');
-
-        $debug_info[] = sprintf('*Operating System:* `%s`', php_uname());
-
-        if (isset($_SERVER['SERVER_SOFTWARE'])) {
-            $debug_info[] = sprintf('*Web Server:* `%s`', $_SERVER['SERVER_SOFTWARE']);
-        }
-        if (function_exists('curl_init')) {
-            $curlversion  = curl_version();
-            $debug_info[] = sprintf('*curl version:* `%1$s; %2$s`', $curlversion['version'], $curlversion['ssl_version']);
-        }
-
-        $webhook_info_title = '*Webhook Info:*';
-        try {
-            // Check if we're actually using the Webhook method.
-            if (Request::getInput() === '') {
-                $debug_info[] = $webhook_info_title . ' `Using getUpdates method, not Webhook.`';
-            } else {
-                $webhook_info_result = json_decode(Request::getWebhookInfo(), true)['result'];
-                // Add a human-readable error date string if necessary.
-                if (isset($webhook_info_result['last_error_date'])) {
-                    $webhook_info_result['last_error_date_string'] = date('Y-m-d H:i:s', $webhook_info_result['last_error_date']);
-                }
-
-                $webhook_info_result_str = json_encode($webhook_info_result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
-                $debug_info[]            = $webhook_info_title;
-                $debug_info[]            = sprintf(
-                    '```' . PHP_EOL . '%s```',
-                    $webhook_info_result_str
-                );
-            }
-        } catch (Exception $e) {
-            $debug_info[] = $webhook_info_title . sprintf(' `Failed to get webhook info! (%s)`', $e->getMessage());
-        }
-
-        $data['parse_mode'] = 'Markdown';
-        $data['text']       = implode(PHP_EOL, $debug_info);
-
-        return Request::sendMessage($data);
-    }
-}
diff --git a/src/Commands/AdminCommands/SendtoallCommand.php b/src/Commands/AdminCommands/SendtoallCommand.php
deleted file mode 100644
index f92e4fb0a..000000000
--- a/src/Commands/AdminCommands/SendtoallCommand.php
+++ /dev/null
@@ -1,113 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\AdminCommands;
-
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\Entities\Message;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-
-/**
- * Admin "/sendtoall" command
- */
-class SendtoallCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'sendtoall';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Send the message to all of the bot\'s users';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/sendtoall <message to send>';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.5.0';
-
-    /**
-     * @var bool
-     */
-    protected $need_mysql = true;
-
-    /**
-     * Execute command
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        $text = $this->getMessage()->getText(true);
-
-        if ($text === '') {
-            return $this->replyToChat('Usage: ' . $this->getUsage());
-        }
-
-        /** @var ServerResponse[] $results */
-        $results = Request::sendToActiveChats(
-            'sendMessage',     //callback function to execute (see Request.php methods)
-            ['text' => $text], //Param to evaluate the request
-            [
-                'groups'      => true,
-                'supergroups' => true,
-                'channels'    => false,
-                'users'       => true,
-            ]
-        );
-
-        if (empty($results)) {
-            return $this->replyToChat('No users or chats found.');
-        }
-
-        $total  = 0;
-        $failed = 0;
-
-        $text = 'Message sent to:' . PHP_EOL;
-
-        foreach ($results as $result) {
-            $name = '';
-            $type = '';
-            if ($result->isOk()) {
-                $status = '✔️';
-
-                /** @var Message $message */
-                $message = $result->getResult();
-                $chat    = $message->getChat();
-                if ($chat->isPrivateChat()) {
-                    $name = $chat->getFirstName();
-                    $type = 'user';
-                } else {
-                    $name = $chat->getTitle();
-                    $type = 'chat';
-                }
-            } else {
-                $status = '✖️';
-                ++$failed;
-            }
-            ++$total;
-
-            $text .= $total . ') ' . $status . ' ' . $type . ' ' . $name . PHP_EOL;
-        }
-        $text .= 'Delivered: ' . ($total - $failed) . '/' . $total . PHP_EOL;
-
-        return $this->replyToChat($text);
-    }
-}
diff --git a/src/Commands/AdminCommands/SendtochannelCommand.php b/src/Commands/AdminCommands/SendtochannelCommand.php
deleted file mode 100644
index 12fb82842..000000000
--- a/src/Commands/AdminCommands/SendtochannelCommand.php
+++ /dev/null
@@ -1,379 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\AdminCommands;
-
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\Conversation;
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\Keyboard;
-use Longman\TelegramBot\Entities\Message;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-
-class SendtochannelCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'sendtochannel';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Send message to a channel';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/sendtochannel <message to send>';
-
-    /**
-     * @var string
-     */
-    protected $version = '0.3.0';
-
-    /**
-     * @var bool
-     */
-    protected $need_mysql = true;
-
-    /**
-     * Conversation Object
-     *
-     * @var Conversation
-     */
-    protected $conversation;
-
-    /**
-     * Command execute method
-     *
-     * @return ServerResponse|mixed
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        $chat_id = $message->getChat()->getId();
-        $user_id = $message->getFrom()->getId();
-
-        $type = $message->getType();
-        // 'Cast' the command type to message to protect the machine state
-        // if the command is recalled when the conversation is already started
-        in_array($type, ['command', 'text'], true) && $type = 'message';
-
-        $text           = trim($message->getText(true));
-        $text_yes_or_no = ($text === 'Yes' || $text === 'No');
-
-        $data = [
-            'chat_id' => $chat_id,
-        ];
-
-        // Conversation
-        $this->conversation = new Conversation($user_id, $chat_id, $this->getName());
-
-        $notes = &$this->conversation->notes;
-        !is_array($notes) && $notes = [];
-
-        $channels = (array) $this->getConfig('your_channel');
-        if (isset($notes['state'])) {
-            $state = $notes['state'];
-        } else {
-            $state                    = (count($channels) === 0) ? -1 : 0;
-            $notes['last_message_id'] = $message->getMessageId();
-        }
-
-        $yes_no_keyboard = new Keyboard(
-            [
-                'keyboard'          => [['Yes', 'No']],
-                'resize_keyboard'   => true,
-                'one_time_keyboard' => true,
-                'selective'         => true,
-            ]
-        );
-
-        switch ($state) {
-            case -1:
-                // getConfig has not been configured asking for channel to administer
-                if ($type !== 'message' || $text === '') {
-                    $notes['state'] = -1;
-                    $this->conversation->update();
-
-                    $result = $this->replyToChat(
-                        'Insert the channel name or ID (_@yourchannel_ or _-12345_)',
-                        [
-                            'parse_mode'   => 'markdown',
-                            'reply_markup' => Keyboard::remove(['selective' => true]),
-                        ]
-                    );
-
-                    break;
-                }
-                $notes['channel']         = $text;
-                $notes['last_message_id'] = $message->getMessageId();
-                // Jump to state 1
-                goto insert;
-
-            // no break
-            default:
-            case 0:
-                // getConfig has been configured choose channel
-                if ($type !== 'message' || $text === '') {
-                    $notes['state'] = 0;
-                    $this->conversation->update();
-
-                    $keyboard = array_map(function ($channel) {
-                        return [$channel];
-                    }, $channels);
-
-                    $result = $this->replyToChat(
-                        'Choose a channel from the keyboard' . PHP_EOL .
-                        '_or_ insert the channel name or ID (_@yourchannel_ or _-12345_)',
-                        [
-                            'parse_mode'   => 'markdown',
-                            'reply_markup' => new Keyboard(
-                                [
-                                    'keyboard'          => $keyboard,
-                                    'resize_keyboard'   => true,
-                                    'one_time_keyboard' => true,
-                                    'selective'         => true,
-                                ]
-                            ),
-                        ]
-                    );
-                    break;
-                }
-                $notes['channel']         = $text;
-                $notes['last_message_id'] = $message->getMessageId();
-
-            // no break
-            case 1:
-                insert:
-                if (($type === 'message' && $text === '') || $notes['last_message_id'] === $message->getMessageId()) {
-                    $notes['state'] = 1;
-                    $this->conversation->update();
-
-                    $result = $this->replyToChat(
-                        'Insert the content you want to share: text, photo, audio...',
-                        ['reply_markup' => Keyboard::remove(['selective' => true])]
-                    );
-                    break;
-                }
-                $notes['last_message_id'] = $message->getMessageId();
-                $notes['message']         = $message->getRawData();
-                $notes['message_type']    = $type;
-            // no break
-            case 2:
-                if (!$text_yes_or_no || $notes['last_message_id'] === $message->getMessageId()) {
-                    $notes['state'] = 2;
-                    $this->conversation->update();
-
-                    // Grab any existing caption.
-                    if ($caption = $message->getCaption()) {
-                        $notes['caption'] = $caption;
-                        $text             = 'No';
-                    } elseif (in_array($notes['message_type'], ['video', 'photo'], true)) {
-                        $text = 'Would you like to insert a caption?';
-                        if (!$text_yes_or_no && $notes['last_message_id'] !== $message->getMessageId()) {
-                            $text .= PHP_EOL . 'Type Yes or No';
-                        }
-
-                        $result = $this->replyToChat(
-                            $text,
-                            ['reply_markup' => $yes_no_keyboard]
-                        );
-                        break;
-                    }
-                }
-                $notes['set_caption']     = ($text === 'Yes');
-                $notes['last_message_id'] = $message->getMessageId();
-            // no break
-            case 3:
-                if ($notes['set_caption'] && ($notes['last_message_id'] === $message->getMessageId() || $type !== 'message')) {
-                    $notes['state'] = 3;
-                    $this->conversation->update();
-
-                    $result = $this->replyToChat(
-                        'Insert caption:',
-                        ['reply_markup' => Keyboard::remove(['selective' => true])]
-                    );
-                    break;
-                }
-                $notes['last_message_id'] = $message->getMessageId();
-                if (isset($notes['caption'])) {
-                    // If caption has already been send with the file, no need to ask for it.
-                    $notes['set_caption'] = true;
-                } else {
-                    $notes['caption'] = $text;
-                }
-            // no break
-            case 4:
-                if (!$text_yes_or_no || $notes['last_message_id'] === $message->getMessageId()) {
-                    $notes['state'] = 4;
-                    $this->conversation->update();
-
-                    $result = $this->replyToChat('Message will look like this:');
-
-                    if ($notes['message_type'] !== 'command') {
-                        if ($notes['set_caption']) {
-                            $data['caption'] = $notes['caption'];
-                        }
-                        $this->sendBack(new Message($notes['message'], $this->telegram->getBotUsername()), $data);
-
-                        $data['reply_markup'] = $yes_no_keyboard;
-
-                        $data['text'] = 'Would you like to post it?';
-                        if (!$text_yes_or_no && $notes['last_message_id'] !== $message->getMessageId()) {
-                            $data['text'] .= PHP_EOL . 'Type Yes or No';
-                        }
-                        $result = Request::sendMessage($data);
-                    }
-                    break;
-                }
-
-                $notes['post_message']    = ($text === 'Yes');
-                $notes['last_message_id'] = $message->getMessageId();
-            // no break
-            case 5:
-                $data['reply_markup'] = Keyboard::remove(['selective' => true]);
-
-                if ($notes['post_message']) {
-                    $data['parse_mode'] = 'markdown';
-                    $data['text']       = $this->publish(
-                        new Message($notes['message'], $this->telegram->getBotUsername()),
-                        $notes['channel'],
-                        $notes['caption']
-                    );
-                } else {
-                    $data['text'] = 'Aborted by user, message not sent..';
-                }
-
-                $this->conversation->stop();
-                $result = Request::sendMessage($data);
-        }
-
-        return $result;
-    }
-
-    /**
-     * SendBack
-     *
-     * Received a message, the bot can send a copy of it to another chat/channel.
-     * You don't have to care about the type of the message, the function detect it and use the proper
-     * REQUEST:: function to send it.
-     * $data include all the var that you need to send the message to the proper chat
-     *
-     * @todo This method will be moved to a higher level maybe in AdminCommand or Command
-     * @todo Looking for a more significant name
-     *
-     * @param Message $message
-     * @param array   $data
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    protected function sendBack(Message $message, array $data): ServerResponse
-    {
-        $type = $message->getType();
-        in_array($type, ['command', 'text'], true) && $type = 'message';
-
-        if ($type === 'message') {
-            $data['text'] = $message->getText(true);
-        } elseif ($type === 'audio') {
-            $data['audio']     = $message->getAudio()->getFileId();
-            $data['duration']  = $message->getAudio()->getDuration();
-            $data['performer'] = $message->getAudio()->getPerformer();
-            $data['title']     = $message->getAudio()->getTitle();
-        } elseif ($type === 'document') {
-            $data['document'] = $message->getDocument()->getFileId();
-        } elseif ($type === 'photo') {
-            $data['photo'] = $message->getPhoto()[0]->getFileId();
-        } elseif ($type === 'sticker') {
-            $data['sticker'] = $message->getSticker()->getFileId();
-        } elseif ($type === 'video') {
-            $data['video'] = $message->getVideo()->getFileId();
-        } elseif ($type === 'voice') {
-            $data['voice'] = $message->getVoice()->getFileId();
-        } elseif ($type === 'location') {
-            $data['latitude']  = $message->getLocation()->getLatitude();
-            $data['longitude'] = $message->getLocation()->getLongitude();
-        }
-
-        return Request::send('send' . ucfirst($type), $data);
-    }
-
-    /**
-     * Publish a message to a channel and return success or failure message in markdown format
-     *
-     * @param Message     $message
-     * @param string|int  $channel_id
-     * @param string|null $caption
-     *
-     * @return string
-     * @throws TelegramException
-     */
-    protected function publish(Message $message, $channel_id, $caption = null): string
-    {
-        $res = $this->sendBack($message, [
-            'chat_id' => $channel_id,
-            'caption' => $caption,
-        ]);
-
-        if ($res->isOk()) {
-            /** @var Chat $channel */
-            $channel          = $res->getResult()->getChat();
-            $escaped_username = $channel->getUsername() ? Entity::escapeMarkdown($channel->getUsername()) : '';
-
-            $response = sprintf(
-                'Message successfully sent to *%s*%s',
-                filter_var($channel->getTitle(), FILTER_SANITIZE_SPECIAL_CHARS),
-                $escaped_username ? " (@{$escaped_username})" : ''
-            );
-        } else {
-            $escaped_username = Entity::escapeMarkdown($channel_id);
-            $response         = "Message not sent to *{$escaped_username}*" . PHP_EOL .
-                                '- Does the channel exist?' . PHP_EOL .
-                                '- Is the bot an admin of the channel?';
-        }
-
-        return $response;
-    }
-
-    /**
-     * Execute without db
-     *
-     * @todo Why send just to the first found channel?
-     *
-     * @return mixed
-     * @throws TelegramException
-     */
-    public function executeNoDb(): ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        $text    = trim($message->getText(true));
-
-        if ($text === '') {
-            return $this->replyToChat('Usage: ' . $this->getUsage());
-        }
-
-        $channels = array_filter((array) $this->getConfig('your_channel'));
-        if (empty($channels)) {
-            return $this->replyToChat('No channels defined in the command config!');
-        }
-
-        return $this->replyToChat($this->publish(
-            new Message($message->getRawData(), $this->telegram->getBotUsername()),
-            reset($channels)
-        ), ['parse_mode' => 'markdown']);
-    }
-}
diff --git a/src/Commands/AdminCommands/WhoisCommand.php b/src/Commands/AdminCommands/WhoisCommand.php
deleted file mode 100644
index 033dbc87a..000000000
--- a/src/Commands/AdminCommands/WhoisCommand.php
+++ /dev/null
@@ -1,190 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- *
- * Written by Jack'lul <jacklul@jacklul.com>
- */
-
-namespace Longman\TelegramBot\Commands\AdminCommands;
-
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\PhotoSize;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Entities\UserProfilePhotos;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-
-/**
- * Admin "/whois" command
- */
-class WhoisCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'whois';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Lookup user or group info';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/whois <id> or /whois <search string>';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.3.0';
-
-    /**
-     * @var bool
-     */
-    protected $need_mysql = true;
-
-    /**
-     * Command execute method
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-
-        $chat_id = $message->getChat()->getId();
-        $command = $message->getCommand();
-        $text    = trim($message->getText(true));
-
-        $data = ['chat_id' => $chat_id];
-
-        //No point in replying to messages in private chats
-        if (!$message->getChat()->isPrivateChat()) {
-            $data['reply_to_message_id'] = $message->getMessageId();
-        }
-
-        if ($command !== 'whois') {
-            $text = substr($command, 5);
-
-            //We need that '-' now, bring it back
-            if (strpos($text, 'g') === 0) {
-                $text = str_replace('g', '-', $text);
-            }
-        }
-
-        if ($text === '') {
-            $text = 'Provide the id to lookup: /whois <id>';
-        } else {
-            $user_id    = $text;
-            $chat       = null;
-            $created_at = null;
-            $updated_at = null;
-            $result     = null;
-
-            if (is_numeric($text)) {
-                $results = DB::selectChats([
-                    'groups'      => true,
-                    'supergroups' => true,
-                    'channels'    => true,
-                    'users'       => true,
-                    'chat_id'     => $user_id, //Specific chat_id to select
-                ]);
-
-                if (!empty($results)) {
-                    $result = reset($results);
-                }
-            } else {
-                $results = DB::selectChats([
-                    'groups'      => true,
-                    'supergroups' => true,
-                    'channels'    => true,
-                    'users'       => true,
-                    'text'        => $text //Text to search in user/group name
-                ]);
-
-                if (is_array($results) && count($results) === 1) {
-                    $result = reset($results);
-                }
-            }
-
-            if (is_array($result)) {
-                $result['id']       = $result['chat_id'];
-                $result['username'] = $result['chat_username'];
-                $chat               = new Chat($result);
-
-                $user_id    = $result['id'];
-                $created_at = $result['chat_created_at'];
-                $updated_at = $result['chat_updated_at'];
-                $old_id     = $result['old_id'];
-            }
-
-            if ($chat !== null) {
-                if ($chat->isPrivateChat()) {
-                    $text = 'User ID: ' . $user_id . PHP_EOL;
-                    $text .= 'Name: ' . $chat->getFirstName() . ' ' . $chat->getLastName() . PHP_EOL;
-
-                    $username = $chat->getUsername();
-                    if ($username !== null && $username !== '') {
-                        $text .= 'Username: @' . $username . PHP_EOL;
-                    }
-
-                    $text .= 'First time seen: ' . $created_at . PHP_EOL;
-                    $text .= 'Last activity: ' . $updated_at . PHP_EOL;
-
-                    //Code from Whoami command
-                    $limit    = 10;
-                    $offset   = null;
-                    $response = Request::getUserProfilePhotos(
-                        [
-                            'user_id' => $user_id,
-                            'limit'   => $limit,
-                            'offset'  => $offset,
-                        ]
-                    );
-
-                    if ($response->isOk()) {
-                        /** @var UserProfilePhotos $user_profile_photos */
-                        $user_profile_photos = $response->getResult();
-
-                        if ($user_profile_photos->getTotalCount() > 0) {
-                            $photos = $user_profile_photos->getPhotos();
-
-                            /** @var PhotoSize $photo */
-                            $photo   = $photos[0][2];
-                            $file_id = $photo->getFileId();
-
-                            $data['photo']   = $file_id;
-                            $data['caption'] = $text;
-
-                            return Request::sendPhoto($data);
-                        }
-                    }
-                } elseif ($chat->isGroupChat()) {
-                    $text = 'Chat ID: ' . $user_id . (!empty($old_id) ? ' (previously: ' . $old_id . ')' : '') . PHP_EOL;
-                    $text .= 'Type: ' . ucfirst($chat->getType()) . PHP_EOL;
-                    $text .= 'Title: ' . $chat->getTitle() . PHP_EOL;
-                    $text .= 'First time added to group: ' . $created_at . PHP_EOL;
-                    $text .= 'Last activity: ' . $updated_at . PHP_EOL;
-                }
-            } elseif (is_array($results) && count($results) > 1) {
-                $text = 'Multiple chats matched!';
-            } else {
-                $text = 'Chat not found!';
-            }
-        }
-
-        $data['text'] = $text;
-
-        return Request::sendMessage($data);
-    }
-}
diff --git a/src/Commands/Command.php b/src/Commands/Command.php
deleted file mode 100644
index 0d5325086..000000000
--- a/src/Commands/Command.php
+++ /dev/null
@@ -1,456 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands;
-
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\CallbackQuery;
-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\Payments\PreCheckoutQuery;
-use Longman\TelegramBot\Entities\Payments\ShippingQuery;
-use Longman\TelegramBot\Entities\Poll;
-use Longman\TelegramBot\Entities\PollAnswer;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-use Longman\TelegramBot\Telegram;
-
-/**
- * Class Command
- *
- * Base class for commands. It includes some helper methods that can fetch data directly from the Update object.
- *
- * @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.
- */
-abstract class Command
-{
-    /**
-     * Auth level for user commands
-     */
-    public const AUTH_USER = 'User';
-
-    /**
-     * Auth level for system commands
-     */
-    public const AUTH_SYSTEM = 'System';
-
-    /**
-     * Auth level for admin commands
-     */
-    public const AUTH_ADMIN = 'Admin';
-
-    /**
-     * Telegram object
-     *
-     * @var Telegram
-     */
-    protected $telegram;
-
-    /**
-     * Update object
-     *
-     * @var Update
-     */
-    protected $update;
-
-    /**
-     * Name
-     *
-     * @var string
-     */
-    protected $name = '';
-
-    /**
-     * Description
-     *
-     * @var string
-     */
-    protected $description = 'Command description';
-
-    /**
-     * Usage
-     *
-     * @var string
-     */
-    protected $usage = 'Command usage';
-
-    /**
-     * Show in Help
-     *
-     * @var bool
-     */
-    protected $show_in_help = true;
-
-    /**
-     * Version
-     *
-     * @var string
-     */
-    protected $version = '1.0.0';
-
-    /**
-     * If this command is enabled
-     *
-     * @var bool
-     */
-    protected $enabled = true;
-
-    /**
-     * If this command needs mysql
-     *
-     * @var bool
-     */
-    protected $need_mysql = false;
-
-    /**
-     * Make sure this command only executes on a private chat.
-     *
-     * @var bool
-     */
-    protected $private_only = false;
-
-    /**
-     * Command config
-     *
-     * @var array
-     */
-    protected $config = [];
-
-    /**
-     * Constructor
-     *
-     * @param Telegram    $telegram
-     * @param Update|null $update
-     */
-    public function __construct(Telegram $telegram, ?Update $update = null)
-    {
-        $this->telegram = $telegram;
-        if ($update !== null) {
-            $this->setUpdate($update);
-        }
-        $this->config = $telegram->getCommandConfig($this->name);
-    }
-
-    /**
-     * Set update object
-     *
-     * @param Update $update
-     *
-     * @return Command
-     */
-    public function setUpdate(Update $update): Command
-    {
-        $this->update = $update;
-
-        return $this;
-    }
-
-    /**
-     * Pre-execute command
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function preExecute(): ServerResponse
-    {
-        if ($this->need_mysql && !($this->telegram->isDbEnabled() && DB::isDbConnected())) {
-            return $this->executeNoDb();
-        }
-
-        if ($this->isPrivateOnly() && $this->removeNonPrivateMessage()) {
-            $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-
-            if ($user = $message->getFrom()) {
-                return Request::sendMessage([
-                    'chat_id'    => $user->getId(),
-                    'parse_mode' => 'Markdown',
-                    'text'       => sprintf(
-                        "/%s command is only available in a private chat.\n(`%s`)",
-                        $this->getName(),
-                        $message->getText()
-                    ),
-                ]);
-            }
-
-            return Request::emptyResponse();
-        }
-
-        return $this->execute();
-    }
-
-    /**
-     * Execute command
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    abstract public function execute(): ServerResponse;
-
-    /**
-     * Execution if MySQL is required but not available
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function executeNoDb(): ServerResponse
-    {
-        return $this->replyToChat('Sorry no database connection, unable to execute "' . $this->name . '" command.');
-    }
-
-    /**
-     * Get update object
-     *
-     * @return Update|null
-     */
-    public function getUpdate(): ?Update
-    {
-        return $this->update;
-    }
-
-    /**
-     * Relay any non-existing function calls to Update object.
-     *
-     * This is purely a helper method to make requests from within execute() method easier.
-     *
-     * @param string $name
-     * @param array  $arguments
-     *
-     * @return Command
-     */
-    public function __call(string $name, array $arguments)
-    {
-        if ($this->update === null) {
-            return null;
-        }
-        return call_user_func_array([$this->update, $name], $arguments);
-    }
-
-    /**
-     * Get command config
-     *
-     * Look for config $name if found return it, if not return $default.
-     * If $name is not set return all set config.
-     *
-     * @param string|null $name
-     * @param mixed       $default
-     *
-     * @return mixed
-     */
-    public function getConfig(?string $name = null, $default = null)
-    {
-        if ($name === null) {
-            return $this->config;
-        }
-        return $this->config[$name] ?? $default;
-    }
-
-    /**
-     * Get telegram object
-     *
-     * @return Telegram
-     */
-    public function getTelegram(): Telegram
-    {
-        return $this->telegram;
-    }
-
-    /**
-     * Get usage
-     *
-     * @return string
-     */
-    public function getUsage(): string
-    {
-        return $this->usage;
-    }
-
-    /**
-     * Get version
-     *
-     * @return string
-     */
-    public function getVersion(): string
-    {
-        return $this->version;
-    }
-
-    /**
-     * Get description
-     *
-     * @return string
-     */
-    public function getDescription(): string
-    {
-        return $this->description;
-    }
-
-    /**
-     * Get name
-     *
-     * @return string
-     */
-    public function getName(): string
-    {
-        return $this->name;
-    }
-
-    /**
-     * Get Show in Help
-     *
-     * @return bool
-     */
-    public function showInHelp(): bool
-    {
-        return $this->show_in_help;
-    }
-
-    /**
-     * Check if command is enabled
-     *
-     * @return bool
-     */
-    public function isEnabled(): bool
-    {
-        return $this->enabled;
-    }
-
-    /**
-     * If this command is intended for private chats only.
-     *
-     * @return bool
-     */
-    public function isPrivateOnly(): bool
-    {
-        return $this->private_only;
-    }
-
-    /**
-     * If this is a SystemCommand
-     *
-     * @return bool
-     */
-    public function isSystemCommand(): bool
-    {
-        return ($this instanceof SystemCommand);
-    }
-
-    /**
-     * If this is an AdminCommand
-     *
-     * @return bool
-     */
-    public function isAdminCommand(): bool
-    {
-        return ($this instanceof AdminCommand);
-    }
-
-    /**
-     * If this is a UserCommand
-     *
-     * @return bool
-     */
-    public function isUserCommand(): bool
-    {
-        return ($this instanceof UserCommand);
-    }
-
-    /**
-     * Delete the current message if it has been called in a non-private chat.
-     *
-     * @return bool
-     */
-    protected function removeNonPrivateMessage(): bool
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage();
-
-        if ($message) {
-            $chat = $message->getChat();
-
-            if (!$chat->isPrivateChat()) {
-                // Delete the falsely called command message.
-                Request::deleteMessage([
-                    'chat_id'    => $chat->getId(),
-                    'message_id' => $message->getMessageId(),
-                ]);
-
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Helper to reply to a chat directly.
-     *
-     * @param string $text
-     * @param array  $data
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function replyToChat(string $text, array $data = []): ServerResponse
-    {
-        if ($message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost()) {
-            $reply = [
-                'chat_id' => $message->getChat()->getId(),
-                'text'    => $text,
-            ];
-            
-            if ($message->getIsTopicMessage()) {
-                $reply['message_thread_id'] = $message->getMessageThreadId();
-            }
-            
-            return Request::sendMessage(array_merge($reply, $data));
-        }
-
-        return Request::emptyResponse();
-    }
-
-    /**
-     * Helper to reply to a user directly.
-     *
-     * @param string $text
-     * @param array  $data
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function replyToUser(string $text, array $data = []): ServerResponse
-    {
-        if ($message = $this->getMessage() ?: $this->getEditedMessage()) {
-            return Request::sendMessage(array_merge([
-                'chat_id' => $message->getFrom()->getId(),
-                'text'    => $text,
-            ], $data));
-        }
-
-        return Request::emptyResponse();
-    }
-}
diff --git a/src/Commands/SystemCommand.php b/src/Commands/SystemCommand.php
deleted file mode 100644
index 8bdebf9c5..000000000
--- a/src/Commands/SystemCommand.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands;
-
-use Longman\TelegramBot\Conversation;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-
-abstract class SystemCommand extends Command
-{
-    /**
-     * @var bool Try to execute any deprecated system command.
-     */
-    public static $execute_deprecated = false;
-
-    /**
-     * @{inheritdoc}
-     *
-     * Set to empty string to disallow users calling system commands.
-     */
-    protected $usage = '';
-
-    /**
-     * A system command just executes
-     *
-     * Although system commands should just work and return a successful ServerResponse,
-     * each system command can override this method to add custom functionality.
-     *
-     * @return ServerResponse
-     */
-    public function execute(): ServerResponse
-    {
-        // System command, return empty ServerResponse by default
-        return Request::emptyResponse();
-    }
-
-    /**
-     * Method to execute any active conversation.
-     *
-     * @return ServerResponse|null
-     * @throws TelegramException
-     * @internal
-     */
-    protected function executeActiveConversation(): ?ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        if ($message === null) {
-            return null;
-        }
-
-        $user = $message->getFrom();
-        $chat = $message->getChat();
-        if ($user === null || $chat === null) {
-            return null;
-        }
-
-        // If a conversation is busy, execute the conversation command after handling the message.
-        $conversation = new Conversation($user->getId(), $chat->getId());
-
-        // Fetch conversation command if it exists and execute it.
-        if ($conversation->exists() && ($command = $conversation->getCommand())) {
-            return $this->getTelegram()->executeCommand($command);
-        }
-
-        return null;
-    }
-
-    /**
-     * BC helper method to execute deprecated system commands.
-     *
-     * @return ServerResponse|null
-     * @throws TelegramException
-     * @internal
-     */
-    protected function executeDeprecatedSystemCommand(): ?ServerResponse
-    {
-        $message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        if ($message === null) {
-            return null;
-        }
-
-        // List of service messages previously handled internally.
-        $service_message_getters = [
-            'newchatmembers'        => 'getNewChatMembers',
-            'leftchatmember'        => 'getLeftChatMember',
-            'newchattitle'          => 'getNewChatTitle',
-            'newchatphoto'          => 'getNewChatPhoto',
-            'deletechatphoto'       => 'getDeleteChatPhoto',
-            'groupchatcreated'      => 'getGroupChatCreated',
-            'supergroupchatcreated' => 'getSupergroupChatCreated',
-            'channelchatcreated'    => 'getChannelChatCreated',
-            'migratefromchatid'     => 'getMigrateFromChatId',
-            'migratetochatid'       => 'getMigrateToChatId',
-            'pinnedmessage'         => 'getPinnedMessage',
-            'successfulpayment'     => 'getSuccessfulPayment',
-        ];
-
-        foreach ($service_message_getters as $command => $service_message_getter) {
-            // Let's check if this message is a service message.
-            if ($message->$service_message_getter() === null) {
-                continue;
-            }
-
-            // Make sure the command exists otherwise GenericCommand would be executed.
-            if ($this->getTelegram()->getCommandObject($command) === null) {
-                break;
-            }
-
-            return $this->getTelegram()->executeCommand($command);
-        }
-
-        return null;
-    }
-}
diff --git a/src/Commands/SystemCommands/CallbackqueryCommand.php b/src/Commands/SystemCommands/CallbackqueryCommand.php
deleted file mode 100644
index b580ac1ae..000000000
--- a/src/Commands/SystemCommands/CallbackqueryCommand.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\SystemCommands;
-
-use Longman\TelegramBot\Commands\SystemCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-
-/**
- * Callback query command
- */
-class CallbackqueryCommand extends SystemCommand
-{
-    /**
-     * @var callable[]
-     */
-    protected static $callbacks = [];
-
-    /**
-     * @var string
-     */
-    protected $name = 'callbackquery';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Reply to callback query';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.0.0';
-
-    /**
-     * Command execute method
-     *
-     * @return ServerResponse
-     */
-    public function execute(): ServerResponse
-    {
-        //$callback_query = $this->getCallbackQuery();
-        //$user_id        = $callback_query->getFrom()->getId();
-        //$query_id       = $callback_query->getId();
-        //$query_data     = $callback_query->getData();
-
-        $answer         = null;
-        $callback_query = $this->getCallbackQuery();
-
-        // Call all registered callbacks.
-        foreach (self::$callbacks as $callback) {
-            $answer = $callback($callback_query);
-        }
-
-        return ($answer instanceof ServerResponse) ? $answer : $callback_query->answer();
-    }
-
-    /**
-     * Add a new callback handler for callback queries.
-     *
-     * @param $callback
-     */
-    public static function addCallbackHandler($callback): void
-    {
-        self::$callbacks[] = $callback;
-    }
-}
diff --git a/src/Commands/SystemCommands/GenericCommand.php b/src/Commands/SystemCommands/GenericCommand.php
deleted file mode 100644
index d7cae29eb..000000000
--- a/src/Commands/SystemCommands/GenericCommand.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\SystemCommands;
-
-use Longman\TelegramBot\Commands\SystemCommand;
-
-/**
- * Generic command
- */
-class GenericCommand extends SystemCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'generic';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Handles generic commands or is executed by default when a command is not found';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.1.0';
-}
diff --git a/src/Commands/SystemCommands/GenericmessageCommand.php b/src/Commands/SystemCommands/GenericmessageCommand.php
deleted file mode 100644
index 9ac31957c..000000000
--- a/src/Commands/SystemCommands/GenericmessageCommand.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\SystemCommands;
-
-use Longman\TelegramBot\Commands\SystemCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Request;
-use Longman\TelegramBot\Telegram;
-
-/**
- * Generic message command
- */
-class GenericmessageCommand extends SystemCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = Telegram::GENERIC_MESSAGE_COMMAND;
-
-    /**
-     * @var string
-     */
-    protected $description = 'Handle generic message';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.2.0';
-
-    /**
-     * @var bool
-     */
-    protected $need_mysql = true;
-
-    /**
-     * Execution if MySQL is required but not available
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function executeNoDb(): ServerResponse
-    {
-        // Try to execute any deprecated system commands.
-        if (self::$execute_deprecated && $deprecated_system_command_response = $this->executeDeprecatedSystemCommand()) {
-            return $deprecated_system_command_response;
-        }
-
-        return Request::emptyResponse();
-    }
-
-    /**
-     * Execute command
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function execute(): ServerResponse
-    {
-        // Try to continue any active conversation.
-        if ($active_conversation_response = $this->executeActiveConversation()) {
-            return $active_conversation_response;
-        }
-
-        // Try to execute any deprecated system commands.
-        if (self::$execute_deprecated && $deprecated_system_command_response = $this->executeDeprecatedSystemCommand()) {
-            return $deprecated_system_command_response;
-        }
-
-        return Request::emptyResponse();
-    }
-}
diff --git a/src/Commands/SystemCommands/InlinequeryCommand.php b/src/Commands/SystemCommands/InlinequeryCommand.php
deleted file mode 100644
index 68c1757c7..000000000
--- a/src/Commands/SystemCommands/InlinequeryCommand.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\SystemCommands;
-
-use Longman\TelegramBot\Commands\SystemCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-
-/**
- * Inline query command
- */
-class InlinequeryCommand extends SystemCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'inlinequery';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Reply to inline query';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.0.1';
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     */
-    public function execute(): ServerResponse
-    {
-        //$inline_query = $this->getInlineQuery();
-        //$user_id      = $inline_query->getFrom()->getId();
-        //$query        = $inline_query->getQuery();
-
-        return $this->getInlineQuery()->answer([]);
-    }
-}
diff --git a/src/Commands/UserCommand.php b/src/Commands/UserCommand.php
deleted file mode 100644
index 2fba11236..000000000
--- a/src/Commands/UserCommand.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands;
-
-abstract class UserCommand extends Command
-{
-
-}
diff --git a/src/Commands/UserCommands/StartCommand.php b/src/Commands/UserCommands/StartCommand.php
deleted file mode 100644
index 3f3d4b88f..000000000
--- a/src/Commands/UserCommands/StartCommand.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\UserCommands;
-
-use Longman\TelegramBot\Commands\UserCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Request;
-
-/**
- * Start command
- */
-class StartCommand extends UserCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'start';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Start command';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/start';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.2.0';
-
-    /**
-     * Command execute method
-     *
-     * @return ServerResponse
-     */
-    public function execute(): ServerResponse
-    {
-        //$message = $this->getMessage() ?: $this->getEditedMessage() ?: $this->getChannelPost() ?: $this->getEditedChannelPost();
-        //$chat_id = $message->getChat()->getId();
-        //$user_id = $message->getFrom()->getId();
-
-        return Request::emptyResponse();
-    }
-}
diff --git a/src/Contracts/AllowsBypassingGet.php b/src/Contracts/AllowsBypassingGet.php
new file mode 100644
index 000000000..462abeb34
--- /dev/null
+++ b/src/Contracts/AllowsBypassingGet.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Contracts;
+
+interface AllowsBypassingGet
+{
+    public static function fieldsBypassingGet(): array;
+}
diff --git a/src/Contracts/Factory.php b/src/Contracts/Factory.php
new file mode 100644
index 000000000..b673dc34c
--- /dev/null
+++ b/src/Contracts/Factory.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Contracts;
+
+interface Factory
+{
+    public static function make(array $data): static;
+}
diff --git a/src/Conversation.php b/src/Conversation.php
deleted file mode 100644
index c9bdf76f7..000000000
--- a/src/Conversation.php
+++ /dev/null
@@ -1,264 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot;
-
-use Longman\TelegramBot\Exception\TelegramException;
-
-/**
- * Class Conversation
- *
- * Only one conversation can be active at any one time.
- * A conversation is directly linked to a user, chat and the command that is managing the conversation.
- */
-class Conversation
-{
-    /**
-     * All information fetched from the database
-     *
-     * @var array|null
-     */
-    protected $conversation;
-
-    /**
-     * Notes stored inside the conversation
-     *
-     * @var mixed
-     */
-    protected $protected_notes;
-
-    /**
-     * Notes to be stored
-     *
-     * @var mixed
-     */
-    public $notes;
-
-    /**
-     * Telegram user id
-     *
-     * @var int
-     */
-    protected $user_id;
-
-    /**
-     * Telegram chat id
-     *
-     * @var int
-     */
-    protected $chat_id;
-
-    /**
-     * Command to be executed if the conversation is active
-     *
-     * @var string
-     */
-    protected $command;
-
-    /**
-     * Conversation constructor to initialize a new conversation
-     *
-     * @param int    $user_id
-     * @param int    $chat_id
-     * @param string $command
-     *
-     * @throws TelegramException
-     */
-    public function __construct(int $user_id, int $chat_id, string $command = '')
-    {
-        $this->user_id = $user_id;
-        $this->chat_id = $chat_id;
-        $this->command = $command;
-
-        //Try to load an existing conversation if possible
-        if (!$this->load() && $command !== '') {
-            //A new conversation start
-            $this->start();
-        }
-    }
-
-    /**
-     * Clear all conversation variables.
-     *
-     * @return bool Always return true, to allow this method in an if statement.
-     */
-    protected function clear(): bool
-    {
-        $this->conversation    = null;
-        $this->protected_notes = null;
-        $this->notes           = null;
-
-        return true;
-    }
-
-    /**
-     * Load the conversation from the database
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    protected function load(): bool
-    {
-        //Select an active conversation
-        $conversation = ConversationDB::selectConversation($this->user_id, $this->chat_id, 1);
-        if (isset($conversation[0])) {
-            //Pick only the first element
-            $this->conversation = $conversation[0];
-
-            //Load the command from the conversation if it hasn't been passed
-            $this->command = $this->command ?: $this->conversation['command'];
-
-            if ($this->command !== $this->conversation['command']) {
-                $this->cancel();
-                return false;
-            }
-
-            //Load the conversation notes
-            $this->protected_notes = json_decode($this->conversation['notes'], true);
-            $this->notes           = $this->protected_notes;
-        }
-
-        return $this->exists();
-    }
-
-    /**
-     * Check if the conversation already exists
-     *
-     * @return bool
-     */
-    public function exists(): bool
-    {
-        return $this->conversation !== null;
-    }
-
-    /**
-     * Start a new conversation if the current command doesn't have one yet
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    protected function start(): bool
-    {
-        if (
-            $this->command
-            && !$this->exists()
-            && ConversationDB::insertConversation(
-                $this->user_id,
-                $this->chat_id,
-                $this->command
-            )
-        ) {
-            return $this->load();
-        }
-
-        return false;
-    }
-
-    /**
-     * Delete the current conversation
-     *
-     * Currently the Conversation is not deleted but just set to 'stopped'
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public function stop(): bool
-    {
-        return $this->updateStatus('stopped') && $this->clear();
-    }
-
-    /**
-     * Cancel the current conversation
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public function cancel(): bool
-    {
-        return $this->updateStatus('cancelled') && $this->clear();
-    }
-
-    /**
-     * Update the status of the current conversation
-     *
-     * @param string $status
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    protected function updateStatus(string $status): bool
-    {
-        if ($this->exists()) {
-            $fields = ['status' => $status];
-            $where  = [
-                'id'      => $this->conversation['id'],
-                'status'  => 'active',
-                'user_id' => $this->user_id,
-                'chat_id' => $this->chat_id,
-            ];
-            if (ConversationDB::updateConversation($fields, $where)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Store the array/variable in the database with json_encode() function
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public function update(): bool
-    {
-        if ($this->exists()) {
-            $fields = ['notes' => json_encode($this->notes, JSON_UNESCAPED_UNICODE)];
-            //I can update a conversation whatever the state is
-            $where = ['id' => $this->conversation['id']];
-            if (ConversationDB::updateConversation($fields, $where)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Retrieve the command to execute from the conversation
-     *
-     * @return string
-     */
-    public function getCommand(): string
-    {
-        return $this->command;
-    }
-
-    /**
-     * Retrieve the user id
-     *
-     * @return int
-     */
-    public function getUserId(): int
-    {
-        return $this->user_id;
-    }
-
-    /**
-     * Retrieve the chat id
-     *
-     * @return int
-     */
-    public function getChatId(): int
-    {
-        return $this->chat_id;
-    }
-}
diff --git a/src/ConversationDB.php b/src/ConversationDB.php
deleted file mode 100644
index c0a568bf7..000000000
--- a/src/ConversationDB.php
+++ /dev/null
@@ -1,132 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot;
-
-use Exception;
-use Longman\TelegramBot\Exception\TelegramException;
-use PDO;
-
-class ConversationDB extends DB
-{
-    /**
-     * Initialize conversation table
-     */
-    public static function initializeConversation(): void
-    {
-        if (!defined('TB_CONVERSATION')) {
-            define('TB_CONVERSATION', self::$table_prefix . 'conversation');
-        }
-    }
-
-    /**
-     * Select a conversation from the DB
-     *
-     * @param int $user_id
-     * @param int $chat_id
-     * @param int $limit
-     *
-     * @return array|bool
-     * @throws TelegramException
-     */
-    public static function selectConversation(int $user_id, int $chat_id, $limit = 0)
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sql = '
-              SELECT *
-              FROM `' . TB_CONVERSATION . '`
-              WHERE `status` = :status
-                AND `chat_id` = :chat_id
-                AND `user_id` = :user_id
-            ';
-
-            if ($limit > 0) {
-                $sql .= ' LIMIT :limit';
-            }
-
-            $sth = self::$pdo->prepare($sql);
-
-            $sth->bindValue(':status', 'active');
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':chat_id', $chat_id);
-
-            if ($limit > 0) {
-                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
-            }
-
-            $sth->execute();
-
-            return $sth->fetchAll(PDO::FETCH_ASSOC);
-        } catch (Exception $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert the conversation in the database
-     *
-     * @param int    $user_id
-     * @param int    $chat_id
-     * @param string $command
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public static function insertConversation(int $user_id, int $chat_id, string $command): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('INSERT INTO `' . TB_CONVERSATION . '`
-                (`status`, `user_id`, `chat_id`, `command`, `notes`, `created_at`, `updated_at`)
-                VALUES
-                (:status, :user_id, :chat_id, :command, :notes, :created_at, :updated_at)
-            ');
-
-            $date = self::getTimestamp();
-
-            $sth->bindValue(':status', 'active');
-            $sth->bindValue(':command', $command);
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':notes', '[]');
-            $sth->bindValue(':created_at', $date);
-            $sth->bindValue(':updated_at', $date);
-
-            return $sth->execute();
-        } catch (Exception $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Update a specific conversation
-     *
-     * @param array $fields_values
-     * @param array $where_fields_values
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public static function updateConversation(array $fields_values, array $where_fields_values): bool
-    {
-        // Auto update the update_at field.
-        $fields_values['updated_at'] = self::getTimestamp();
-
-        return self::update(TB_CONVERSATION, $fields_values, $where_fields_values);
-    }
-}
diff --git a/src/DB.php b/src/DB.php
deleted file mode 100644
index cae89be1f..000000000
--- a/src/DB.php
+++ /dev/null
@@ -1,1725 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- * Written by Marco Boretto <marco.bore@gmail.com>
- */
-
-namespace Longman\TelegramBot;
-
-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;
-use Longman\TelegramBot\Entities\PollAnswer;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Entities\User;
-use Longman\TelegramBot\Exception\TelegramException;
-use PDO;
-use PDOException;
-
-class DB
-{
-    /**
-     * MySQL credentials
-     *
-     * @var array
-     */
-    protected static $mysql_credentials = [];
-
-    /**
-     * PDO object
-     *
-     * @var PDO
-     */
-    protected static $pdo;
-
-    /**
-     * Table prefix
-     *
-     * @var string
-     */
-    protected static $table_prefix;
-
-    /**
-     * Telegram class object
-     *
-     * @var Telegram
-     */
-    protected static $telegram;
-
-    /**
-     * Initialize
-     *
-     * @param array    $credentials  Database connection details
-     * @param Telegram $telegram     Telegram object to connect with this object
-     * @param string   $table_prefix Table prefix
-     * @param string   $encoding     Database character encoding
-     *
-     * @return PDO PDO database object
-     * @throws TelegramException
-     */
-    public static function initialize(
-        array $credentials,
-        Telegram $telegram,
-        $table_prefix = '',
-        $encoding = 'utf8mb4',
-    ): PDO {
-        if (empty($credentials)) {
-            throw new TelegramException('MySQL credentials not provided!');
-        }
-        if (isset($credentials['unix_socket'])) {
-            $dsn = 'mysql:unix_socket=' . $credentials['unix_socket'];
-        } else {
-            $dsn = 'mysql:host=' . $credentials['host'];
-        }
-        $dsn .= ';dbname=' . $credentials['database'];
-
-        if (!empty($credentials['port'])) {
-            $dsn .= ';port=' . $credentials['port'];
-        }
-
-        $options = [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES ' . $encoding];
-        try {
-            $pdo = new PDO($dsn, $credentials['user'], $credentials['password'], $options);
-            $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-
-        self::$pdo               = $pdo;
-        self::$telegram          = $telegram;
-        self::$mysql_credentials = $credentials;
-        self::$table_prefix      = $table_prefix;
-
-        self::defineTables();
-
-        return self::$pdo;
-    }
-
-    /**
-     * External Initialize
-     *
-     * Let you use the class with an external already existing Pdo Mysql connection.
-     *
-     * @param PDO      $external_pdo_connection PDO database object
-     * @param Telegram $telegram                Telegram object to connect with this object
-     * @param string   $table_prefix            Table prefix
-     *
-     * @return PDO PDO database object
-     * @throws TelegramException
-     */
-    public static function externalInitialize(
-        PDO $external_pdo_connection,
-        Telegram $telegram,
-        string $table_prefix = '',
-    ): PDO {
-        if ($external_pdo_connection === null) {
-            throw new TelegramException('MySQL external connection not provided!');
-        }
-
-        self::$pdo               = $external_pdo_connection;
-        self::$telegram          = $telegram;
-        self::$mysql_credentials = [];
-        self::$table_prefix      = $table_prefix;
-
-        self::defineTables();
-
-        return self::$pdo;
-    }
-
-    /**
-     * Define all the tables with the proper prefix
-     */
-    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',
-            'request_limiter',
-            'shipping_query',
-            'telegram_update',
-            'user',
-            'user_chat',
-        ];
-        foreach ($tables as $table) {
-            $table_name = 'TB_' . strtoupper($table);
-            if (!defined($table_name)) {
-                define($table_name, self::$table_prefix . $table);
-            }
-        }
-    }
-
-    /**
-     * Check if database connection has been created
-     *
-     * @return bool
-     */
-    public static function isDbConnected(): bool
-    {
-        return self::$pdo !== null;
-    }
-
-    /**
-     * Get the PDO object of the connected database
-     *
-     * @return PDO|null
-     */
-    public static function getPdo(): ?PDO
-    {
-        return self::$pdo;
-    }
-
-    /**
-     * Fetch update(s) from DB
-     *
-     * @param int    $limit Limit the number of updates to fetch
-     * @param string $id    Check for unique update id
-     *
-     * @return array|bool Fetched data or false if not connected
-     * @throws TelegramException
-     */
-    public static function selectTelegramUpdate(int $limit = 0, string $id = '')
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sql = '
-                SELECT `id`
-                FROM `' . TB_TELEGRAM_UPDATE . '`
-            ';
-
-            if ($id !== '') {
-                $sql .= ' WHERE `id` = :id';
-            } else {
-                $sql .= ' ORDER BY `id` DESC';
-            }
-
-            if ($limit > 0) {
-                $sql .= ' LIMIT :limit';
-            }
-
-            $sth = self::$pdo->prepare($sql);
-
-            if ($limit > 0) {
-                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
-            }
-            if ($id !== '') {
-                $sth->bindValue(':id', $id);
-            }
-
-            $sth->execute();
-
-            return $sth->fetchAll(PDO::FETCH_ASSOC);
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Fetch message(s) from DB
-     *
-     * @param int $limit Limit the number of messages to fetch
-     *
-     * @return array|bool Fetched data or false if not connected
-     * @throws TelegramException
-     */
-    public static function selectMessages(int $limit = 0)
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sql = '
-                SELECT *
-                FROM `' . TB_MESSAGE . '`
-                ORDER BY `id` DESC
-            ';
-
-            if ($limit > 0) {
-                $sql .= ' LIMIT :limit';
-            }
-
-            $sth = self::$pdo->prepare($sql);
-
-            if ($limit > 0) {
-                $sth->bindValue(':limit', $limit, PDO::PARAM_INT);
-            }
-
-            $sth->execute();
-
-            return $sth->fetchAll(PDO::FETCH_ASSOC);
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Convert from unix timestamp to timestamp
-     *
-     * @param ?int $unixtime Unix timestamp (if empty, current timestamp is used)
-     *
-     * @return string
-     */
-    protected static function getTimestamp(?int $unixtime = null): string
-    {
-        return date('Y-m-d H:i:s', $unixtime ?? time());
-    }
-
-    /**
-     * Convert array of Entity items to a JSON array
-     *
-     * @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)
-    {
-        if (empty($entities)) {
-            return $default;
-        }
-
-        // Convert each Entity item into an object based on its JSON reflection
-        $json_entities = array_map(function ($entity) {
-            return json_decode($entity, true);
-        }, $entities);
-
-        return json_encode($json_entities);
-    }
-
-    /**
-     * Insert entry to telegram_update table
-     *
-     * @throws TelegramException
-     */
-    protected static function insertTelegramUpdate(
-        int $update_id,
-        ?int $chat_id = null,
-        ?int $message_id = null,
-        ?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,
-        ?string $shipping_query_id = null,
-        ?string $pre_checkout_query_id = null,
-        ?string $poll_id = null,
-        ?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_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 && $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()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_TELEGRAM_UPDATE . '`
-                (
-                    `id`, `chat_id`, `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`
-                ) VALUES (
-                    :id, :chat_id, :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
-                )
-            ');
-
-            $sth->bindValue(':id', $update_id);
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':message_id', $message_id);
-            $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);
-            $sth->bindValue(':shipping_query_id', $shipping_query_id);
-            $sth->bindValue(':pre_checkout_query_id', $pre_checkout_query_id);
-            $sth->bindValue(':poll_id', $poll_id);
-            $sth->bindValue(':poll_answer_poll_id', $poll_answer_poll_id);
-            $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) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert users and save their connection to chats
-     *
-     * @param User        $user
-     * @param string|null $date
-     * @param Chat|null   $chat
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertUser(User $user, ?string $date = null, ?Chat $chat = null): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT INTO `' . TB_USER . '`
-                (`id`, `is_bot`, `username`, `first_name`, `last_name`, `language_code`, `is_premium`, `added_to_attachment_menu`, `created_at`, `updated_at`)
-                VALUES
-                (:id, :is_bot, :username, :first_name, :last_name, :language_code, :is_premium, :added_to_attachment_menu, :created_at, :updated_at)
-                ON DUPLICATE KEY UPDATE
-                    `is_bot`                   = VALUES(`is_bot`),
-                    `username`                 = VALUES(`username`),
-                    `first_name`               = VALUES(`first_name`),
-                    `last_name`                = VALUES(`last_name`),
-                    `language_code`            = VALUES(`language_code`),
-                    `is_premium`               = VALUES(`is_premium`),
-                    `added_to_attachment_menu` = VALUES(`added_to_attachment_menu`),
-                    `updated_at`               = VALUES(`updated_at`)
-            ');
-
-            $sth->bindValue(':id', $user->getId());
-            $sth->bindValue(':is_bot', $user->getIsBot(), PDO::PARAM_INT);
-            $sth->bindValue(':username', $user->getUsername());
-            $sth->bindValue(':first_name', $user->getFirstName());
-            $sth->bindValue(':last_name', $user->getLastName());
-            $sth->bindValue(':language_code', $user->getLanguageCode());
-            $sth->bindValue(':is_premium', $user->getIsPremium(), PDO::PARAM_INT);
-            $sth->bindValue(':added_to_attachment_menu', $user->getAddedToAttachmentMenu(), PDO::PARAM_INT);
-            $date = $date ?: self::getTimestamp();
-            $sth->bindValue(':created_at', $date);
-            $sth->bindValue(':updated_at', $date);
-
-            $status = $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-
-        // Also insert the relationship to the chat into the user_chat table
-        if ($chat) {
-            try {
-                $sth = self::$pdo->prepare('
-                    INSERT IGNORE INTO `' . TB_USER_CHAT . '`
-                    (`user_id`, `chat_id`)
-                    VALUES
-                    (:user_id, :chat_id)
-                ');
-
-                $sth->bindValue(':user_id', $user->getId());
-                $sth->bindValue(':chat_id', $chat->getId());
-
-                $status = $sth->execute();
-            } catch (PDOException $e) {
-                throw new TelegramException($e->getMessage());
-            }
-        }
-
-        return $status;
-    }
-
-    /**
-     * Insert chat
-     *
-     * @param Chat        $chat
-     * @param string|null $date
-     * @param int|null    $migrate_to_chat_id
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertChat(Chat $chat, ?string $date = null, ?int $migrate_to_chat_id = null): ?bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_CHAT . '`
-                (`id`, `type`, `title`, `username`, `first_name`, `last_name`, `is_forum`, `created_at` ,`updated_at`, `old_id`)
-                VALUES
-                (:id, :type, :title, :username, :first_name, :last_name, :is_forum, :created_at, :updated_at, :old_id)
-                ON DUPLICATE KEY UPDATE
-                    `type`                           = VALUES(`type`),
-                    `title`                          = VALUES(`title`),
-                    `username`                       = VALUES(`username`),
-                    `first_name`                     = VALUES(`first_name`),
-                    `last_name`                      = VALUES(`last_name`),
-                    `is_forum`                       = VALUES(`is_forum`),
-                    `updated_at`                     = VALUES(`updated_at`)
-            ');
-
-            $chat_id   = $chat->getId();
-            $chat_type = $chat->getType();
-
-            if ($migrate_to_chat_id !== null) {
-                $chat_type = 'supergroup';
-
-                $sth->bindValue(':id', $migrate_to_chat_id);
-                $sth->bindValue(':old_id', $chat_id);
-            } else {
-                $sth->bindValue(':id', $chat_id);
-                $sth->bindValue(':old_id', $migrate_to_chat_id);
-            }
-
-            $sth->bindValue(':type', $chat_type);
-            $sth->bindValue(':title', $chat->getTitle());
-            $sth->bindValue(':username', $chat->getUsername());
-            $sth->bindValue(':first_name', $chat->getFirstName());
-            $sth->bindValue(':last_name', $chat->getLastName());
-            $sth->bindValue(':is_forum', $chat->getIsForum());
-            $date = $date ?: self::getTimestamp();
-            $sth->bindValue(':created_at', $date);
-            $sth->bindValue(':updated_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert request into database
-     *
-     * @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
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        $chat_id                   = null;
-        $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;
-
-        if (($message = $update->getMessage()) && self::insertMessageRequest($message)) {
-            $chat_id    = $message->getChat()->getId();
-            $message_id = $message->getMessageId();
-        } elseif (($edited_message = $update->getEditedMessage()) && self::insertEditedMessageRequest($edited_message)) {
-            $chat_id           = $edited_message->getChat()->getId();
-            $edited_message_id = (int) self::$pdo->lastInsertId();
-        } elseif (($channel_post = $update->getChannelPost()) && self::insertMessageRequest($channel_post)) {
-            $chat_id         = $channel_post->getChat()->getId();
-            $channel_post_id = $channel_post->getMessageId();
-        } 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)) {
-            $chosen_inline_result_id = self::$pdo->lastInsertId();
-        } elseif (($callback_query = $update->getCallbackQuery()) && self::insertCallbackQueryRequest($callback_query)) {
-            $callback_query_id = $callback_query->getId();
-        } elseif (($shipping_query = $update->getShippingQuery()) && self::insertShippingQueryRequest($shipping_query)) {
-            $shipping_query_id = $shipping_query->getId();
-        } elseif (($pre_checkout_query = $update->getPreCheckoutQuery()) && self::insertPreCheckoutQueryRequest($pre_checkout_query)) {
-            $pre_checkout_query_id = $pre_checkout_query->getId();
-        } elseif (($poll = $update->getPoll()) && self::insertPollRequest($poll)) {
-            $poll_id = $poll->getId();
-        } elseif (($poll_answer = $update->getPollAnswer()) && self::insertPollAnswerRequest($poll_answer)) {
-            $poll_answer_poll_id = $poll_answer->getPollId();
-        } elseif (($my_chat_member = $update->getMyChatMember()) && self::insertChatMemberUpdatedRequest($my_chat_member)) {
-            $my_chat_member_updated_id = self::$pdo->lastInsertId();
-        } elseif (($chat_member = $update->getChatMember()) && self::insertChatMemberUpdatedRequest($chat_member)) {
-            $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;
-        }
-
-        return self::insertTelegramUpdate(
-            $update->getUpdateId(),
-            $chat_id,
-            $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,
-        );
-    }
-
-    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
-     *
-     * @param InlineQuery $inline_query
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertInlineQueryRequest(InlineQuery $inline_query): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_INLINE_QUERY . '`
-                (`id`, `user_id`, `location`, `query`, `offset`, `chat_type`, `created_at`)
-                VALUES
-                (:id, :user_id, :location, :query, :offset, :chat_type, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $user_id = null;
-
-            if ($user = $inline_query->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':id', $inline_query->getId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':location', $inline_query->getLocation());
-            $sth->bindValue(':query', $inline_query->getQuery());
-            $sth->bindValue(':offset', $inline_query->getOffset());
-            $sth->bindValue(':chat_type', $inline_query->getChatType());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert chosen inline result request into database
-     *
-     * @param ChosenInlineResult $chosen_inline_result
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertChosenInlineResultRequest(ChosenInlineResult $chosen_inline_result): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT INTO `' . TB_CHOSEN_INLINE_RESULT . '`
-                (`result_id`, `user_id`, `location`, `inline_message_id`, `query`, `created_at`)
-                VALUES
-                (:result_id, :user_id, :location, :inline_message_id, :query, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $user_id = null;
-
-            if ($user = $chosen_inline_result->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':result_id', $chosen_inline_result->getResultId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':location', $chosen_inline_result->getLocation());
-            $sth->bindValue(':inline_message_id', $chosen_inline_result->getInlineMessageId());
-            $sth->bindValue(':query', $chosen_inline_result->getQuery());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert callback query request into database
-     *
-     * @param CallbackQuery $callback_query
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertCallbackQueryRequest(CallbackQuery $callback_query): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_CALLBACK_QUERY . '`
-                (`id`, `user_id`, `chat_id`, `message_id`, `inline_message_id`, `chat_instance`, `data`, `game_short_name`, `created_at`)
-                VALUES
-                (:id, :user_id, :chat_id, :message_id, :inline_message_id, :chat_instance, :data, :game_short_name, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $user_id = null;
-
-            if ($user = $callback_query->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $chat_id    = null;
-            $message_id = null;
-            if ($message = $callback_query->getMessage()) {
-                $chat_id    = $message->getChat()->getId();
-                $message_id = $message->getMessageId();
-
-                $is_message = self::$pdo->query('
-                    SELECT *
-                    FROM `' . TB_MESSAGE . '`
-                    WHERE `id` = ' . $message_id . '
-                      AND `chat_id` = ' . $chat_id . '
-                    LIMIT 1
-                ')->rowCount();
-
-                if ($is_message) {
-                    self::insertEditedMessageRequest($message);
-                } else {
-                    self::insertMessageRequest($message);
-                }
-            }
-
-            $sth->bindValue(':id', $callback_query->getId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':message_id', $message_id);
-            $sth->bindValue(':inline_message_id', $callback_query->getInlineMessageId());
-            $sth->bindValue(':chat_instance', $callback_query->getChatInstance());
-            $sth->bindValue(':data', $callback_query->getData());
-            $sth->bindValue(':game_short_name', $callback_query->getGameShortName());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert shipping query request into database
-     *
-     * @param ShippingQuery $shipping_query
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertShippingQueryRequest(ShippingQuery $shipping_query): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_SHIPPING_QUERY . '`
-                (`id`, `user_id`, `invoice_payload`, `shipping_address`, `created_at`)
-                VALUES
-                (:id, :user_id, :invoice_payload, :shipping_address, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $user_id = null;
-
-            if ($user = $shipping_query->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':id', $shipping_query->getId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':invoice_payload', $shipping_query->getInvoicePayload());
-            $sth->bindValue(':shipping_address', $shipping_query->getShippingAddress());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert pre checkout query request into database
-     *
-     * @param PreCheckoutQuery $pre_checkout_query
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertPreCheckoutQueryRequest(PreCheckoutQuery $pre_checkout_query): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_PRE_CHECKOUT_QUERY . '`
-                (`id`, `user_id`, `currency`, `total_amount`, `invoice_payload`, `shipping_option_id`, `order_info`, `created_at`)
-                VALUES
-                (:id, :user_id, :currency, :total_amount, :invoice_payload, :shipping_option_id, :order_info, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $user_id = null;
-
-            if ($user = $pre_checkout_query->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':id', $pre_checkout_query->getId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':currency', $pre_checkout_query->getCurrency());
-            $sth->bindValue(':total_amount', $pre_checkout_query->getTotalAmount());
-            $sth->bindValue(':invoice_payload', $pre_checkout_query->getInvoicePayload());
-            $sth->bindValue(':shipping_option_id', $pre_checkout_query->getShippingOptionId());
-            $sth->bindValue(':order_info', $pre_checkout_query->getOrderInfo());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert poll request into database
-     *
-     * @param Poll $poll
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertPollRequest(Poll $poll): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT INTO `' . TB_POLL . '`
-                (`id`, `question`, `options`, `total_voter_count`, `is_closed`, `is_anonymous`, `type`, `allows_multiple_answers`, `correct_option_id`, `explanation`, `explanation_entities`, `open_period`, `close_date`, `created_at`)
-                VALUES
-                (:id, :question, :options, :total_voter_count, :is_closed, :is_anonymous, :type, :allows_multiple_answers, :correct_option_id, :explanation, :explanation_entities, :open_period, :close_date, :created_at)
-                ON DUPLICATE KEY UPDATE
-                    `options`                 = VALUES(`options`),
-                    `total_voter_count`       = VALUES(`total_voter_count`),
-                    `is_closed`               = VALUES(`is_closed`),
-                    `is_anonymous`            = VALUES(`is_anonymous`),
-                    `type`                    = VALUES(`type`),
-                    `allows_multiple_answers` = VALUES(`allows_multiple_answers`),
-                    `correct_option_id`       = VALUES(`correct_option_id`),
-                    `explanation`             = VALUES(`explanation`),
-                    `explanation_entities`    = VALUES(`explanation_entities`),
-                    `open_period`             = VALUES(`open_period`),
-                    `close_date`              = VALUES(`close_date`)
-            ');
-
-            $sth->bindValue(':id', $poll->getId());
-            $sth->bindValue(':question', $poll->getQuestion());
-            $sth->bindValue(':options', self::entitiesArrayToJson($poll->getOptions() ?: []));
-            $sth->bindValue(':total_voter_count', $poll->getTotalVoterCount());
-            $sth->bindValue(':is_closed', $poll->getIsClosed(), PDO::PARAM_INT);
-            $sth->bindValue(':is_anonymous', $poll->getIsAnonymous(), PDO::PARAM_INT);
-            $sth->bindValue(':type', $poll->getType());
-            $sth->bindValue(':allows_multiple_answers', $poll->getAllowsMultipleAnswers(), PDO::PARAM_INT);
-            $sth->bindValue(':correct_option_id', $poll->getCorrectOptionId());
-            $sth->bindValue(':explanation', $poll->getExplanation());
-            $sth->bindValue(':explanation_entities', self::entitiesArrayToJson($poll->getExplanationEntities() ?: []));
-            $sth->bindValue(':open_period', $poll->getOpenPeriod());
-            $sth->bindValue(':close_date', self::getTimestamp($poll->getCloseDate()));
-            $sth->bindValue(':created_at', self::getTimestamp());
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert poll answer request into database
-     *
-     * @param PollAnswer $poll_answer
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertPollAnswerRequest(PollAnswer $poll_answer): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT INTO `' . TB_POLL_ANSWER . '`
-                (`poll_id`, `user_id`, `option_ids`, `created_at`)
-                VALUES
-                (:poll_id, :user_id, :option_ids, :created_at)
-                ON DUPLICATE KEY UPDATE
-                    `option_ids` = VALUES(`option_ids`)
-            ');
-
-            $date    = self::getTimestamp();
-            $user_id = null;
-
-            if ($user = $poll_answer->getUser()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':poll_id', $poll_answer->getPollId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':option_ids', json_encode($poll_answer->getOptionIds()));
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert chat member updated request into database
-     *
-     * @param ChatMemberUpdated $chat_member_updated
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertChatMemberUpdatedRequest(ChatMemberUpdated $chat_member_updated): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT INTO `' . TB_CHAT_MEMBER_UPDATED . '`
-                (`chat_id`, `user_id`, `date`, `old_chat_member`, `new_chat_member`, `invite_link`, `created_at`)
-                VALUES
-                (:chat_id, :user_id, :date, :old_chat_member, :new_chat_member, :invite_link, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $chat_id = null;
-            $user_id = null;
-
-            if ($chat = $chat_member_updated->getChat()) {
-                $chat_id = $chat->getId();
-                self::insertChat($chat, $date);
-            }
-            if ($user = $chat_member_updated->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':date', self::getTimestamp($chat_member_updated->getDate()));
-            $sth->bindValue(':old_chat_member', $chat_member_updated->getOldChatMember());
-            $sth->bindValue(':new_chat_member', $chat_member_updated->getNewChatMember());
-            $sth->bindValue(':invite_link', $chat_member_updated->getInviteLink());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert chat join request into database
-     *
-     * @param ChatJoinRequest $chat_join_request
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertChatJoinRequestRequest(ChatJoinRequest $chat_join_request): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT INTO `' . TB_CHAT_JOIN_REQUEST . '`
-                (`chat_id`, `user_id`, `date`, `bio`, `invite_link`, `created_at`)
-                VALUES
-                (:chat_id, :user_id, :date, :bio, :invite_link, :created_at)
-            ');
-
-            $date    = self::getTimestamp();
-            $chat_id = null;
-            $user_id = null;
-
-            if ($chat = $chat_join_request->getChat()) {
-                $chat_id = $chat->getId();
-                self::insertChat($chat, $date);
-            }
-            if ($user = $chat_join_request->getFrom()) {
-                $user_id = $user->getId();
-                self::insertUser($user, $date);
-            }
-
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':date', self::getTimestamp($chat_join_request->getDate()));
-            $sth->bindValue(':bio', $chat_join_request->getBio());
-            $sth->bindValue(':invite_link', $chat_join_request->getInviteLink());
-            $sth->bindValue(':created_at', $date);
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * 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
-     *
-     * @param Message $message
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertMessageRequest(Message $message): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        $date = self::getTimestamp($message->getDate());
-
-        // Insert chat, update chat id in case it migrated
-        $chat = $message->getChat();
-        self::insertChat($chat, $date, $message->getMigrateToChatId());
-
-        $sender_chat_id = null;
-        if ($sender_chat = $message->getSenderChat()) {
-            self::insertChat($sender_chat);
-            $sender_chat_id = $sender_chat->getId();
-        }
-
-        // Insert user and the relation with the chat
-        if ($user = $message->getFrom()) {
-            self::insertUser($user, $date, $chat);
-        }
-
-        // Insert the forwarded message user in users table
-        $forward_date = $message->getForwardDate() ? self::getTimestamp($message->getForwardDate()) : null;
-
-        if ($forward_from = $message->getForwardFrom()) {
-            self::insertUser($forward_from);
-            $forward_from = $forward_from->getId();
-        }
-        if ($forward_from_chat = $message->getForwardFromChat()) {
-            self::insertChat($forward_from_chat);
-            $forward_from_chat = $forward_from_chat->getId();
-        }
-
-        $via_bot_id = null;
-        if ($via_bot = $message->getViaBot()) {
-            self::insertUser($via_bot);
-            $via_bot_id = $via_bot->getId();
-        }
-
-        // New and left chat member
-        $new_chat_members_ids = null;
-        $left_chat_member_id  = null;
-
-        $new_chat_members = $message->getNewChatMembers();
-        $left_chat_member = $message->getLeftChatMember();
-        if (!empty($new_chat_members)) {
-            foreach ($new_chat_members as $new_chat_member) {
-                if ($new_chat_member instanceof User) {
-                    // Insert the new chat user
-                    self::insertUser($new_chat_member, $date, $chat);
-                    $new_chat_members_ids[] = $new_chat_member->getId();
-                }
-            }
-            $new_chat_members_ids = implode(',', $new_chat_members_ids);
-        } elseif ($left_chat_member) {
-            // Insert the left chat user
-            self::insertUser($left_chat_member, $date, $chat);
-            $left_chat_member_id = $left_chat_member->getId();
-        }
-
-        try {
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_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`, `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`, `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, :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, :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
-                )
-            ');
-
-            $user_id = $user ? $user->getId() : null;
-            $chat_id = $chat->getId();
-
-            $reply_to_message_id = null;
-            if ($reply_to_message = $message->getReplyToMessage()) {
-                $reply_to_message_id = $reply_to_message->getMessageId();
-                // please notice that, as explained in the documentation, reply_to_message don't contain other
-                // reply_to_message field so recursion deep is 1
-                self::insertMessageRequest($reply_to_message);
-            }
-
-            $sth->bindValue(':message_id', $message->getMessageId());
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':sender_chat_id', $sender_chat_id);
-            $sth->bindValue(':message_thread_id', $message->getMessageThreadId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':date', $date);
-            $sth->bindValue(':forward_from', $forward_from);
-            $sth->bindValue(':forward_from_chat', $forward_from_chat);
-            $sth->bindValue(':forward_from_message_id', $message->getForwardFromMessageId());
-            $sth->bindValue(':forward_signature', $message->getForwardSignature());
-            $sth->bindValue(':forward_sender_name', $message->getForwardSenderName());
-            $sth->bindValue(':forward_date', $forward_date);
-            $sth->bindValue(':is_topic_message', $message->getIsTopicMessage());
-
-            $reply_to_chat_id = null;
-            if ($reply_to_message_id !== null) {
-                $reply_to_chat_id = $chat_id;
-            }
-            $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());
-            $sth->bindValue(':text', $message->getText());
-            $sth->bindValue(':entities', self::entitiesArrayToJson($message->getEntities() ?: []));
-            $sth->bindValue(':caption_entities', self::entitiesArrayToJson($message->getCaptionEntities() ?: []));
-            $sth->bindValue(':audio', $message->getAudio());
-            $sth->bindValue(':document', $message->getDocument());
-            $sth->bindValue(':animation', $message->getAnimation());
-            $sth->bindValue(':game', $message->getGame());
-            $sth->bindValue(':photo', self::entitiesArrayToJson($message->getPhoto() ?: []));
-            $sth->bindValue(':sticker', $message->getSticker());
-            $sth->bindValue(':story', $message->getStory());
-            $sth->bindValue(':video', $message->getVideo());
-            $sth->bindValue(':voice', $message->getVoice());
-            $sth->bindValue(':video_note', $message->getVideoNote());
-            $sth->bindValue(':caption', $message->getCaption());
-            $sth->bindValue(':has_media_spoiler', $message->getHasMediaSpoiler());
-            $sth->bindValue(':contact', $message->getContact());
-            $sth->bindValue(':location', $message->getLocation());
-            $sth->bindValue(':venue', $message->getVenue());
-            $sth->bindValue(':poll', $message->getPoll());
-            $sth->bindValue(':dice', $message->getDice());
-            $sth->bindValue(':new_chat_members', $new_chat_members_ids);
-            $sth->bindValue(':left_chat_member', $left_chat_member_id);
-            $sth->bindValue(':new_chat_title', $message->getNewChatTitle());
-            $sth->bindValue(':new_chat_photo', self::entitiesArrayToJson($message->getNewChatPhoto() ?: []));
-            $sth->bindValue(':delete_chat_photo', $message->getDeleteChatPhoto());
-            $sth->bindValue(':group_chat_created', $message->getGroupChatCreated());
-            $sth->bindValue(':supergroup_chat_created', $message->getSupergroupChatCreated());
-            $sth->bindValue(':channel_chat_created', $message->getChannelChatCreated());
-            $sth->bindValue(':message_auto_delete_timer_changed', $message->getMessageAutoDeleteTimerChanged());
-            $sth->bindValue(':migrate_to_chat_id', $message->getMigrateToChatId());
-            $sth->bindValue(':migrate_from_chat_id', $message->getMigrateFromChatId());
-            $sth->bindValue(':pinned_message', $message->getPinnedMessage());
-            $sth->bindValue(':invoice', $message->getInvoice());
-            $sth->bindValue(':successful_payment', $message->getSuccessfulPayment());
-            $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());
-            $sth->bindValue(':passport_data', $message->getPassportData());
-            $sth->bindValue(':proximity_alert_triggered', $message->getProximityAlertTriggered());
-            $sth->bindValue(':forum_topic_created', $message->getForumTopicCreated());
-            $sth->bindValue(':forum_topic_edited', $message->getForumTopicEdited());
-            $sth->bindValue(':forum_topic_closed', $message->getForumTopicClosed());
-            $sth->bindValue(':forum_topic_reopened', $message->getForumTopicReopened());
-            $sth->bindValue(':general_forum_topic_hidden', $message->getGeneralForumTopicHidden());
-            $sth->bindValue(':general_forum_topic_unhidden', $message->getGeneralForumTopicUnhidden());
-            $sth->bindValue(':video_chat_scheduled', $message->getVideoChatScheduled());
-            $sth->bindValue(':video_chat_started', $message->getVideoChatStarted());
-            $sth->bindValue(':video_chat_ended', $message->getVideoChatEnded());
-            $sth->bindValue(':video_chat_participants_invited', $message->getVideoChatParticipantsInvited());
-            $sth->bindValue(':web_app_data', $message->getWebAppData());
-            $sth->bindValue(':reply_markup', $message->getReplyMarkup());
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert Edited Message request in db
-     *
-     * @param Message $edited_message
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertEditedMessageRequest(Message $edited_message): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $edit_date = self::getTimestamp($edited_message->getEditDate());
-
-            // Insert chat
-            $chat = $edited_message->getChat();
-            self::insertChat($chat, $edit_date);
-
-            // Insert user and the relation with the chat
-            if ($user = $edited_message->getFrom()) {
-                self::insertUser($user, $edit_date, $chat);
-            }
-
-            $sth = self::$pdo->prepare('
-                INSERT IGNORE INTO `' . TB_EDITED_MESSAGE . '`
-                (`chat_id`, `message_id`, `user_id`, `edit_date`, `text`, `entities`, `caption`)
-                VALUES
-                (:chat_id, :message_id, :user_id, :edit_date, :text, :entities, :caption)
-            ');
-
-            $user_id = $user ? $user->getId() : null;
-
-            $sth->bindValue(':chat_id', $chat->getId());
-            $sth->bindValue(':message_id', $edited_message->getMessageId());
-            $sth->bindValue(':user_id', $user_id);
-            $sth->bindValue(':edit_date', $edit_date);
-            $sth->bindValue(':text', $edited_message->getText());
-            $sth->bindValue(':entities', self::entitiesArrayToJson($edited_message->getEntities() ?: []));
-            $sth->bindValue(':caption', $edited_message->getCaption());
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Select Groups, Supergroups, Channels and/or single user Chats (also by ID or text)
-     *
-     * @param $select_chats_params
-     *
-     * @return array|bool
-     * @throws TelegramException
-     */
-    public static function selectChats($select_chats_params)
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        // Set defaults for omitted values.
-        $select = array_merge([
-            'groups'      => true,
-            'supergroups' => true,
-            'channels'    => true,
-            'users'       => true,
-            'date_from'   => null,
-            'date_to'     => null,
-            'chat_id'     => null,
-            'text'        => null,
-            'language'    => null,
-        ], $select_chats_params);
-
-        if (!$select['groups'] && !$select['users'] && !$select['supergroups'] && !$select['channels']) {
-            return false;
-        }
-
-        try {
-            $query = '
-                SELECT * ,
-                ' . TB_CHAT . '.`id` AS `chat_id`,
-                ' . TB_CHAT . '.`username` AS `chat_username`,
-                ' . TB_CHAT . '.`created_at` AS `chat_created_at`,
-                ' . TB_CHAT . '.`updated_at` AS `chat_updated_at`
-            ';
-            if ($select['users']) {
-                $query .= '
-                    , ' . TB_USER . '.`id` AS `user_id`
-                    FROM `' . TB_CHAT . '`
-                    LEFT JOIN `' . TB_USER . '`
-                    ON ' . TB_CHAT . '.`id`=' . TB_USER . '.`id`
-                ';
-            } else {
-                $query .= 'FROM `' . TB_CHAT . '`';
-            }
-
-            // Building parts of query
-            $where  = [];
-            $tokens = [];
-
-            if (!$select['groups'] || !$select['users'] || !$select['supergroups'] || !$select['channels']) {
-                $chat_or_user = [];
-
-                $select['groups'] && $chat_or_user[] = TB_CHAT . '.`type` = "group"';
-                $select['supergroups'] && $chat_or_user[] = TB_CHAT . '.`type` = "supergroup"';
-                $select['channels'] && $chat_or_user[] = TB_CHAT . '.`type` = "channel"';
-                $select['users'] && $chat_or_user[] = TB_CHAT . '.`type` = "private"';
-
-                $where[] = '(' . implode(' OR ', $chat_or_user) . ')';
-            }
-
-            if (null !== $select['date_from']) {
-                $where[]              = TB_CHAT . '.`updated_at` >= :date_from';
-                $tokens[':date_from'] = $select['date_from'];
-            }
-
-            if (null !== $select['date_to']) {
-                $where[]            = TB_CHAT . '.`updated_at` <= :date_to';
-                $tokens[':date_to'] = $select['date_to'];
-            }
-
-            if (null !== $select['chat_id']) {
-                $where[]            = TB_CHAT . '.`id` = :chat_id';
-                $tokens[':chat_id'] = $select['chat_id'];
-            }
-
-            if ($select['users'] && null !== $select['language']) {
-                $where[]             = TB_USER . '.`language_code` = :language';
-                $tokens[':language'] = $select['language'];
-            }
-
-            if (null !== $select['text']) {
-                $text_like = '%' . strtolower($select['text']) . '%';
-                if ($select['users']) {
-                    $where[]          = '(
-                        LOWER(' . TB_CHAT . '.`title`) LIKE :text1
-                        OR LOWER(' . TB_USER . '.`first_name`) LIKE :text2
-                        OR LOWER(' . TB_USER . '.`last_name`) LIKE :text3
-                        OR LOWER(' . TB_USER . '.`username`) LIKE :text4
-                    )';
-                    $tokens[':text1'] = $text_like;
-                    $tokens[':text2'] = $text_like;
-                    $tokens[':text3'] = $text_like;
-                    $tokens[':text4'] = $text_like;
-                } else {
-                    $where[]         = 'LOWER(' . TB_CHAT . '.`title`) LIKE :text';
-                    $tokens[':text'] = $text_like;
-                }
-            }
-
-            if (!empty($where)) {
-                $query .= ' WHERE ' . implode(' AND ', $where);
-            }
-
-            $query .= ' ORDER BY ' . TB_CHAT . '.`updated_at` ASC';
-
-            $sth = self::$pdo->prepare($query);
-            $sth->execute($tokens);
-
-            return $sth->fetchAll(PDO::FETCH_ASSOC);
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Get Telegram API request count for current chat / message
-     *
-     * @param int|string|null $chat_id
-     * @param string|null     $inline_message_id
-     *
-     * @return array|bool Array containing TOTAL and CURRENT fields or false on invalid arguments
-     * @throws TelegramException
-     */
-    public static function getTelegramRequestCount($chat_id = null, string $inline_message_id = null)
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('SELECT
-                (SELECT COUNT(DISTINCT `chat_id`) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_1) AS LIMIT_PER_SEC_ALL,
-                (SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_2 AND ((`chat_id` = :chat_id_1 AND `inline_message_id` IS NULL) OR (`inline_message_id` = :inline_message_id AND `chat_id` IS NULL))) AS LIMIT_PER_SEC,
-                (SELECT COUNT(*) FROM `' . TB_REQUEST_LIMITER . '` WHERE `created_at` >= :created_at_minute AND `chat_id` = :chat_id_2) AS LIMIT_PER_MINUTE
-            ');
-
-            $date        = self::getTimestamp();
-            $date_minute = self::getTimestamp(strtotime('-1 minute'));
-
-            $sth->bindValue(':chat_id_1', $chat_id);
-            $sth->bindValue(':chat_id_2', $chat_id);
-            $sth->bindValue(':inline_message_id', $inline_message_id);
-            $sth->bindValue(':created_at_1', $date);
-            $sth->bindValue(':created_at_2', $date);
-            $sth->bindValue(':created_at_minute', $date_minute);
-
-            $sth->execute();
-
-            return $sth->fetch();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Insert Telegram API request in db
-     *
-     * @param string $method
-     * @param array  $data
-     *
-     * @return bool If the insert was successful
-     * @throws TelegramException
-     */
-    public static function insertTelegramRequest(string $method, array $data): bool
-    {
-        if (!self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            $sth = self::$pdo->prepare('INSERT INTO `' . TB_REQUEST_LIMITER . '`
-                (`method`, `chat_id`, `inline_message_id`, `created_at`)
-                VALUES
-                (:method, :chat_id, :inline_message_id, :created_at);
-            ');
-
-            $chat_id           = $data['chat_id'] ?? null;
-            $inline_message_id = $data['inline_message_id'] ?? null;
-
-            $sth->bindValue(':chat_id', $chat_id);
-            $sth->bindValue(':inline_message_id', $inline_message_id);
-            $sth->bindValue(':method', $method);
-            $sth->bindValue(':created_at', self::getTimestamp());
-
-            return $sth->execute();
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-
-    /**
-     * Bulk update the entries of any table
-     *
-     * @param string $table
-     * @param array  $fields_values
-     * @param array  $where_fields_values
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public static function update(string $table, array $fields_values, array $where_fields_values): bool
-    {
-        if (empty($fields_values) || !self::isDbConnected()) {
-            return false;
-        }
-
-        try {
-            // Building parts of query
-            $tokens = $fields = $where = [];
-
-            // Fields with values to update
-            foreach ($fields_values as $field => $value) {
-                $token          = ':' . count($tokens);
-                $fields[]       = "`{$field}` = {$token}";
-                $tokens[$token] = $value;
-            }
-
-            // Where conditions
-            foreach ($where_fields_values as $field => $value) {
-                $token          = ':' . count($tokens);
-                $where[]        = "`{$field}` = {$token}";
-                $tokens[$token] = $value;
-            }
-
-            $sql = 'UPDATE `' . $table . '` SET ' . implode(', ', $fields);
-            $sql .= count($where) > 0 ? ' WHERE ' . implode(' AND ', $where) : '';
-
-            return self::$pdo->prepare($sql)->execute($tokens);
-        } catch (PDOException $e) {
-            throw new TelegramException($e->getMessage());
-        }
-    }
-}
diff --git a/src/Entities/Animation.php b/src/Entities/Animation.php
index 83b012a29..561a2368d 100644
--- a/src/Entities/Animation.php
+++ b/src/Entities/Animation.php
@@ -1,39 +1,21 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Animation
- *
- * You can provide an animation for your game so that it looks stylish in chats (check out Lumberjack for an example). This object represents an animation file to be displayed in the message containing a game.
- *
- * @link https://core.telegram.org/bots/api#animation
- *
- * @method string    getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string    getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int       getWidth()        Video width as defined by sender
- * @method int       getHeight()       Video height as defined by sender
- * @method int       getDuration()     Duration of the video in seconds as defined by sender
- * @method PhotoSize getThumbnail()    Optional. Animation thumbnail as defined by sender
- * @method string    getFileName()     Optional. Original animation filename as defined by sender
- * @method string    getMimeType()     Optional. MIME type of the file as defined by sender
- * @method int       getFileSize()     Optional. File size
- **/
+ * @method string         getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string         getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int            getWidth()        Video width as defined by sender
+ * @method int            getHeight()       Video height as defined by sender
+ * @method int            getDuration()     Duration of the video in seconds as defined by sender
+ * @method PhotoSize|null getThumbnail()    Optional. Animation thumbnail as defined by sender
+ * @method string|null    getFileName()     Optional. Original animation filename as defined by sender
+ * @method string|null    getMimeType()     Optional. MIME type of the file as defined by sender
+ * @method int|null       getFileSize()     Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.
+ */
 class Animation extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'thumbnail' => PhotoSize::class,
diff --git a/src/Entities/Audio.php b/src/Entities/Audio.php
index 91d1359ba..6689e4a68 100644
--- a/src/Entities/Audio.php
+++ b/src/Entities/Audio.php
@@ -1,37 +1,21 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Audio
- *
- * @link https://core.telegram.org/bots/api#audio
- *
- * @method string    getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string    getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int       getDuration()     Duration of the audio in seconds as defined by sender
- * @method string    getPerformer()    Optional. Performer of the audio as defined by sender or by audio tags
- * @method string    getTitle()        Optional. Title of the audio as defined by sender or by audio tags
- * @method string    getFileName()     Optional. Original filename as defined by sender
- * @method string    getMimeType()     Optional. MIME type of the file as defined by sender
- * @method int       getFileSize()     Optional. File size
- * @method PhotoSize getThumbnail()    Optional. Thumbnail of the album cover to which the music file belongs
+ * @method string         getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string         getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int            getDuration()     Duration of the audio in seconds as defined by sender
+ * @method string|null    getPerformer()    Optional. Performer of the audio as defined by sender or by audio tags
+ * @method string|null    getTitle()        Optional. Title of the audio as defined by sender or by audio tags
+ * @method string|null    getFileName()     Optional. Original filename as defined by sender
+ * @method string|null    getMimeType()     Optional. MIME type of the file as defined by sender
+ * @method int|null       getFileSize()     Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.
+ * @method PhotoSize|null getThumbnail()    Optional. Thumbnail of the album cover to which the music file belongs
  */
 class Audio extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'thumbnail' => PhotoSize::class,
diff --git a/src/Entities/BackgroundFill/BackgroundFill.php b/src/Entities/BackgroundFill/BackgroundFill.php
new file mode 100644
index 000000000..abeaad853
--- /dev/null
+++ b/src/Entities/BackgroundFill/BackgroundFill.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundFill;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the background fill
+ */
+class BackgroundFill extends Entity implements Factory
+{
+    public const TYPE_SOLID = 'solid';
+
+    public const TYPE_GRADIENT = 'gradient';
+
+    public const TYPE_FREEFORM_GRADIENT = 'freeform_gradient';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_SOLID             => new BackgroundFillSolid($data),
+            self::TYPE_GRADIENT          => new BackgroundFillGradient($data),
+            self::TYPE_FREEFORM_GRADIENT => new BackgroundFillFreeformGradient($data),
+        };
+    }
+}
diff --git a/src/Entities/BackgroundFill/BackgroundFillFreeformGradient.php b/src/Entities/BackgroundFill/BackgroundFillFreeformGradient.php
new file mode 100644
index 000000000..0e9784a97
--- /dev/null
+++ b/src/Entities/BackgroundFill/BackgroundFillFreeformGradient.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundFill;
+
+/**
+ * @method int[] getColors() A list of the 3 or 4 base colors that are used to generate the freeform gradient in the RGB24 format
+ */
+class BackgroundFillFreeformGradient extends BackgroundFill
+{
+    //
+}
diff --git a/src/Entities/BackgroundFill/BackgroundFillGradient.php b/src/Entities/BackgroundFill/BackgroundFillGradient.php
new file mode 100644
index 000000000..e178ad4ba
--- /dev/null
+++ b/src/Entities/BackgroundFill/BackgroundFillGradient.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundFill;
+
+/**
+ * @method int getTopColor()      Top color of the gradient in the RGB24 format
+ * @method int getBottomColor()   Bottom color of the gradient in the RGB24 format
+ * @method int getRotationAngle() Clockwise rotation angle of the background fill in degrees; 0-359
+ */
+class BackgroundFillGradient extends BackgroundFill
+{
+    //
+}
diff --git a/src/Entities/BackgroundFill/BackgroundFillSolid.php b/src/Entities/BackgroundFill/BackgroundFillSolid.php
new file mode 100644
index 000000000..c5171c6f9
--- /dev/null
+++ b/src/Entities/BackgroundFill/BackgroundFillSolid.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundFill;
+
+/**
+ * @method int getColor() The color of the background fill in the RGB24 format
+ */
+class BackgroundFillSolid extends BackgroundFill
+{
+    //
+}
diff --git a/src/Entities/BackgroundType/BackgroundType.php b/src/Entities/BackgroundType/BackgroundType.php
new file mode 100644
index 000000000..5dada44d7
--- /dev/null
+++ b/src/Entities/BackgroundType/BackgroundType.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundType;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the background
+ */
+abstract class BackgroundType extends Entity implements Factory
+{
+    public const TYPE_FILL = 'fill';
+
+    public const TYPE_WALLPAPER = 'wallpaper';
+
+    public const TYPE_PATTERN = 'pattern';
+
+    public const TYPE_CHAT_THEME = 'chat_theme';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_FILL       => new BackgroundTypeFill($data),
+            self::TYPE_WALLPAPER  => new BackgroundTypeWallpaper($data),
+            self::TYPE_PATTERN    => new BackgroundTypePattern($data),
+            self::TYPE_CHAT_THEME => new BackgroundTypeChatTheme($data),
+        };
+    }
+}
diff --git a/src/Entities/BackgroundType/BackgroundTypeChatTheme.php b/src/Entities/BackgroundType/BackgroundTypeChatTheme.php
new file mode 100644
index 000000000..44574f4e3
--- /dev/null
+++ b/src/Entities/BackgroundType/BackgroundTypeChatTheme.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundType;
+
+/**
+ * @method string getThemeName() Name of the chat theme, which is usually an emoji
+ */
+class BackgroundTypeChatTheme extends BackgroundType
+{
+    //
+}
diff --git a/src/Entities/BackgroundType/BackgroundTypeFill.php b/src/Entities/BackgroundType/BackgroundTypeFill.php
new file mode 100644
index 000000000..ba33e1891
--- /dev/null
+++ b/src/Entities/BackgroundType/BackgroundTypeFill.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundType;
+
+use PhpTelegramBot\Core\Entities\BackgroundFill\BackgroundFill;
+
+/**
+ * @method BackgroundFill getFill()             The background fill
+ * @method int            getDarkThemeDimming() Dimming of the background in dark themes, as a percentage; 0-100
+ */
+class BackgroundTypeFill extends BackgroundType
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'fill' => BackgroundFill::class,
+        ];
+    }
+}
diff --git a/src/Entities/BackgroundType/BackgroundTypePattern.php b/src/Entities/BackgroundType/BackgroundTypePattern.php
new file mode 100644
index 000000000..b1338ef7a
--- /dev/null
+++ b/src/Entities/BackgroundType/BackgroundTypePattern.php
@@ -0,0 +1,33 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundType;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\BackgroundFill\BackgroundFill;
+use PhpTelegramBot\Core\Entities\Document;
+
+/**
+ * @method Document       getDocument()  Document with the pattern
+ * @method BackgroundFill getFill()      The background fill that is combined with the pattern
+ * @method int            getIntensity() Intensity of the pattern when it is shown above the filled background; 0-100
+ * @method bool           isInverted()   Optional. True, if the background fill must be applied only to the pattern itself. All other pixels are black in this case. For dark themes only
+ * @method bool           isMoving()     Optional. True, if the background moves slightly when the device is tilted
+ */
+class BackgroundTypePattern extends BackgroundType implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'document' => Document::class,
+            'fill'     => BackgroundFill::class,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_inverted' => false,
+            'is_moving'   => false,
+        ];
+    }
+}
diff --git a/src/Entities/BackgroundType/BackgroundTypeWallpaper.php b/src/Entities/BackgroundType/BackgroundTypeWallpaper.php
new file mode 100644
index 000000000..fd3f25890
--- /dev/null
+++ b/src/Entities/BackgroundType/BackgroundTypeWallpaper.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\BackgroundType;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\Document;
+
+/**
+ * @method Document getDocument()         Document with the wallpaper
+ * @method int      getDarkThemeDimming() Dimming of the background in dark themes, as a percentage; 0-100
+ * @method bool     isBlurred()           Optional. True, if the wallpaper is downscaled to fit in a 450x450 square and then box-blurred with radius 12
+ * @method bool     isMoving()            Optional. True, if the background moves slightly when the device is tilted
+ */
+class BackgroundTypeWallpaper extends BackgroundType implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'document' => Document::class,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_blurred' => false,
+            'is_moving'  => false,
+        ];
+    }
+}
diff --git a/src/Entities/Birthdate.php b/src/Entities/Birthdate.php
new file mode 100644
index 000000000..1ddaf2b09
--- /dev/null
+++ b/src/Entities/Birthdate.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int      getDay()   Day of the user's birth; 1-31
+ * @method int      getMonth() Month of the user's birth; 1-12
+ * @method int|null getYear()  Optional. Year of the user's birth
+ */
+class Birthdate extends Entity
+{
+    //
+}
diff --git a/src/Entities/BotCommand.php b/src/Entities/BotCommand.php
index dc3302e45..90d88ad72 100644
--- a/src/Entities/BotCommand.php
+++ b/src/Entities/BotCommand.php
@@ -1,27 +1,12 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class BotCommand
- *
- * This entity represents a bot command.
- *
- * @link https://core.telegram.org/bots/api#botcommand
- *
- * @method string getCommand()     Text of the command, 1-32 characters. Can contain only lowercase English letters, digits and underscores.
- * @method string getDescription() Description of the command, 3-256 characters.
+ * @method string getCommand()     Text of the command; 1-32 characters. Can contain only lowercase English letters, digits and underscores.
+ * @method string getDescription() Description of the command; 1-256 characters.
  */
 class BotCommand extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScope.php b/src/Entities/BotCommandScope/BotCommandScope.php
index b0127c710..c869dcd8e 100644
--- a/src/Entities/BotCommandScope/BotCommandScope.php
+++ b/src/Entities/BotCommandScope/BotCommandScope.php
@@ -1,8 +1,39 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
-interface BotCommandScope
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Scope type
+ */
+abstract class BotCommandScope extends Entity implements Factory
 {
+    public const TYPE_DEFAULT = 'default';
+
+    public const TYPE_ALL_PRIVATE_CHATS = 'all_private_chats';
+
+    public const TYPE_ALL_GROUP_CHATS = 'all_group_chats';
+
+    public const TYPE_ALL_CHAT_ADMINISTRATORS = 'all_chat_administrators';
+
+    public const TYPE_CHAT = 'chat';
+
+    public const TYPE_CHAT_ADMINISTRATORS = 'chat_administrators';
+
+    public const TYPE_CHAT_MEMBER = 'chat_member';
 
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_DEFAULT                 => new BotCommandScopeDefault($data),
+            self::TYPE_ALL_PRIVATE_CHATS       => new BotCommandScopeAllPrivateChats($data),
+            self::TYPE_ALL_GROUP_CHATS         => new BotCommandScopeAllGroupChats($data),
+            self::TYPE_ALL_CHAT_ADMINISTRATORS => new BotCommandScopeAllChatAdministrators($data),
+            self::TYPE_CHAT                    => new BotCommandScopeChat($data),
+            self::TYPE_CHAT_ADMINISTRATORS     => new BotCommandScopeChatAdministrators($data),
+            self::TYPE_CHAT_MEMBER             => new BotCommandScopeChatMember($data),
+        };
+    }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeAllChatAdministrators.php b/src/Entities/BotCommandScope/BotCommandScopeAllChatAdministrators.php
index de8d50a14..4277179a0 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeAllChatAdministrators.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeAllChatAdministrators.php
@@ -1,19 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class BotCommandScopeAllChatAdministrators
- *
- * @link https://core.telegram.org/bots/api#botcommandscopeallchatadministrators
- */
-class BotCommandScopeAllChatAdministrators extends Entity implements BotCommandScope
+class BotCommandScopeAllChatAdministrators extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    //
+
+    protected static function presetData(): array
     {
-        $data['type'] = 'all_chat_administrators';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_ALL_CHAT_ADMINISTRATORS,
+        ];
     }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeAllGroupChats.php b/src/Entities/BotCommandScope/BotCommandScopeAllGroupChats.php
index d2e549c82..3dfffb5a6 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeAllGroupChats.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeAllGroupChats.php
@@ -1,19 +1,13 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class BotCommandScopeAllGroupChats
- *
- * @link https://core.telegram.org/bots/api#botcommandscopeallgroupchats
- */
-class BotCommandScopeAllGroupChats extends Entity implements BotCommandScope
+class BotCommandScopeAllGroupChats extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    protected static function presetData(): array
     {
-        $data['type'] = 'all_group_chats';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_ALL_GROUP_CHATS,
+        ];
     }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeAllPrivateChats.php b/src/Entities/BotCommandScope/BotCommandScopeAllPrivateChats.php
index 6fc3a50a2..08f62201a 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeAllPrivateChats.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeAllPrivateChats.php
@@ -1,19 +1,13 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class BotCommandScopeAllPrivateChats
- *
- * @link https://core.telegram.org/bots/api#botcommandscopeallprivatechats
- */
-class BotCommandScopeAllPrivateChats extends Entity implements BotCommandScope
+class BotCommandScopeAllPrivateChats extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    protected static function presetData(): array
     {
-        $data['type'] = 'all_private_chats';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_ALL_PRIVATE_CHATS,
+        ];
     }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeChat.php b/src/Entities/BotCommandScope/BotCommandScopeChat.php
index 011b349e7..22357cef8 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeChat.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeChat.php
@@ -1,30 +1,16 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
-
-use Longman\TelegramBot\Entities\Entity;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
 /**
- * Class BotCommandScopeChat
- *
- * @link https://core.telegram.org/bots/api#botcommandscopechat
- *
- * <code>
- * $data = [
- *   'chat_id' => '123456'
- * ];
- * </code>
- *
- * @method string getType()   Scope type, must be chat
- * @method string getChatId() Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
- *
- * @method $this setChatId(string $chat_id) Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
+ * @method int|string getChatId() Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername).
  */
-class BotCommandScopeChat extends Entity implements BotCommandScope
+class BotCommandScopeChat extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    protected static function presetData(): array
     {
-        $data['type'] = 'chat';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_CHAT,
+        ];
     }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeChatAdministrators.php b/src/Entities/BotCommandScope/BotCommandScopeChatAdministrators.php
index d16588bc4..3b8d626d3 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeChatAdministrators.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeChatAdministrators.php
@@ -1,30 +1,16 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
-
-use Longman\TelegramBot\Entities\Entity;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
 /**
- * Class BotCommandScopeChatAdministrators
- *
- * @link https://core.telegram.org/bots/api#botcommandscopechatadministrators
- *
- * <code>
- * $data = [
- *   'chat_id' => '123456'
- * ];
- * </code>
- *
- * @method string getType()   Scope type, must be chat_administrators
- * @method string getChatId() Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
- *
- * @method $this setChatId(string $chat_id) Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
+ * @method int|string getChatId() Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername).
  */
-class BotCommandScopeChatAdministrators extends Entity implements BotCommandScope
+class BotCommandScopeChatAdministrators extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    protected static function presetData(): array
     {
-        $data['type'] = 'chat_administrators';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_CHAT_ADMINISTRATORS,
+        ];
     }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeChatMember.php b/src/Entities/BotCommandScope/BotCommandScopeChatMember.php
index 2c57acff7..ab7c9038f 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeChatMember.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeChatMember.php
@@ -1,33 +1,17 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
-
-use Longman\TelegramBot\Entities\Entity;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
 /**
- * Class BotCommandScopeChatMember
- *
- * @link https://core.telegram.org/bots/api#botcommandscopechatmember
- *
- * <code>
- * $data = [
- *   'chat_id' => '123456',
- *   'user_id' => 987654,
- * ];
- * </code>
- *
- * @method string getType()   Scope type, must be chat_member
- * @method string getChatId() Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
- * @method int    getUserId() Unique identifier of the target user
- *
- * @method $this setChatId(string $chat_id) Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername)
- * @method $this setUserId(int $user_id)    Unique identifier of the target user
+ * @method int|string getChatId() Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername).
+ * @method int        getUserId() Unique identifier of the target user
  */
-class BotCommandScopeChatMember extends Entity implements BotCommandScope
+class BotCommandScopeChatMember extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    protected static function presetData(): array
     {
-        $data['type'] = 'chat_member';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_CHAT_MEMBER,
+        ];
     }
 }
diff --git a/src/Entities/BotCommandScope/BotCommandScopeDefault.php b/src/Entities/BotCommandScope/BotCommandScopeDefault.php
index f25ef582f..680ba3c25 100644
--- a/src/Entities/BotCommandScope/BotCommandScopeDefault.php
+++ b/src/Entities/BotCommandScope/BotCommandScopeDefault.php
@@ -1,19 +1,13 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\BotCommandScope;
+namespace PhpTelegramBot\Core\Entities\BotCommandScope;
 
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class BotCommandScopeDefault
- *
- * @link https://core.telegram.org/bots/api#botcommandscopedefault
- */
-class BotCommandScopeDefault extends Entity implements BotCommandScope
+class BotCommandScopeDefault extends BotCommandScope
 {
-    public function __construct(array $data = [])
+    protected static function presetData(): array
     {
-        $data['type'] = 'default';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_DEFAULT,
+        ];
     }
 }
diff --git a/src/Entities/BotDescription.php b/src/Entities/BotDescription.php
index ec1d72d27..67977ce0f 100644
--- a/src/Entities/BotDescription.php
+++ b/src/Entities/BotDescription.php
@@ -1,17 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class BotDescription
- *
- * This entity represents the bot's description.
- *
- * @link https://core.telegram.org/bots/api#botdescription
- *
- * @method string getDescription() The bot's description.
+ * @method string getDescription() The bot's description
  */
 class BotDescription extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/BotName.php b/src/Entities/BotName.php
index a36635b43..b3f886a70 100644
--- a/src/Entities/BotName.php
+++ b/src/Entities/BotName.php
@@ -1,17 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class BotName
- *
- * This object represents the bot's name.
- *
- * @link https://core.telegram.org/bots/api#botname
- *
- * @method string getName() The bot's name.
+ * @method string getName() The bot's name
  */
 class BotName extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/BotShortDescription.php b/src/Entities/BotShortDescription.php
index cfc98a76b..1a33d8d1d 100644
--- a/src/Entities/BotShortDescription.php
+++ b/src/Entities/BotShortDescription.php
@@ -1,17 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class BotShortDescription
- *
- * This entity represents the bot's short description.
- *
- * @link https://core.telegram.org/bots/api#botshortdescription
- *
  * @method string getShortDescription() The bot's short description
  */
 class BotShortDescription extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/BusinessConnection.php b/src/Entities/BusinessConnection.php
new file mode 100644
index 000000000..3d12a90cc
--- /dev/null
+++ b/src/Entities/BusinessConnection.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+
+/**
+ * @method string getId()         Unique identifier of the business connection
+ * @method User   getUser()       Business account user that created the business connection
+ * @method int    getUserChatId() Identifier of a private chat with the user who created the business connection. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method int    getDate()       Date the connection was established in Unix time
+ * @method bool   getCanReply()   True, if the bot can act on behalf of the business account in chats that were active in the last 24 hours
+ * @method bool   isEnabled()     True, if the connection is active
+ */
+class BusinessConnection extends Entity implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'user' => User::class,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_enabled' => false,
+        ];
+    }
+}
diff --git a/src/Entities/BusinessIntro.php b/src/Entities/BusinessIntro.php
new file mode 100644
index 000000000..9cdba8a9f
--- /dev/null
+++ b/src/Entities/BusinessIntro.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string|null  getTitle()   Optional. Title text of the business intro
+ * @method string|null  getMessage() Optional. Message text of the business intro
+ * @method Sticker|null getSticker() Optional. Sticker of the business intro
+ */
+class BusinessIntro extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'sticker' => Sticker::class,
+        ];
+    }
+}
diff --git a/src/Entities/BusinessLocation.php b/src/Entities/BusinessLocation.php
new file mode 100644
index 000000000..ac40a3f0f
--- /dev/null
+++ b/src/Entities/BusinessLocation.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string        getAddress()  Address of the business
+ * @method Location|null getLocation() Optional. Location of the business
+ */
+class BusinessLocation extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'location' => Location::class,
+        ];
+    }
+}
diff --git a/src/Entities/BusinessMessagesDeleted.php b/src/Entities/BusinessMessagesDeleted.php
new file mode 100644
index 000000000..371d3d5f4
--- /dev/null
+++ b/src/Entities/BusinessMessagesDeleted.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string getBusinessConnectionId() Unique identifier of the business connection
+ * @method Chat   getChat()                 Information about a chat in the business account. The bot may not have access to the chat or the corresponding user.
+ * @method int[]  getMessageIds()           The list of identifiers of deleted messages in the chat of the business account
+ */
+class BusinessMessagesDeleted extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'chat' => Chat::class,
+        ];
+    }
+}
diff --git a/src/Entities/BusinessOpeningHours.php b/src/Entities/BusinessOpeningHours.php
new file mode 100644
index 000000000..101f03ffa
--- /dev/null
+++ b/src/Entities/BusinessOpeningHours.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string                         getTimeZoneName() Unique name of the time zone for which the opening hours are defined
+ * @method BusinessOpeningHoursInterval[] getOpeningHours() List of time intervals describing business opening hours
+ */
+class BusinessOpeningHours extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'opening_hours' => [BusinessOpeningHoursInterval::class],
+        ];
+    }
+}
diff --git a/src/Entities/BusinessOpeningHoursInterval.php b/src/Entities/BusinessOpeningHoursInterval.php
new file mode 100644
index 000000000..95355ee2c
--- /dev/null
+++ b/src/Entities/BusinessOpeningHoursInterval.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int getOpeningMinute() The minute's sequence number in a week, starting on Monday, marking the start of the time interval during which the business is open; 0 - 7 * 24 * 60
+ * @method int getClosingMinute() The minute's sequence number in a week, starting on Monday, marking the end of the time interval during which the business is open; 0 - 8 * 24 * 60
+ */
+class BusinessOpeningHoursInterval extends Entity
+{
+    //
+}
diff --git a/src/Entities/CallbackGame.php b/src/Entities/CallbackGame.php
new file mode 100644
index 000000000..bbab9a1d5
--- /dev/null
+++ b/src/Entities/CallbackGame.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class CallbackGame extends Entity
+{
+    //
+}
diff --git a/src/Entities/CallbackQuery.php b/src/Entities/CallbackQuery.php
index d5fd51d7e..af7427d20 100644
--- a/src/Entities/CallbackQuery.php
+++ b/src/Entities/CallbackQuery.php
@@ -1,57 +1,23 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * 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\Message\Factory as MaybeInaccessibleMessageFactory;
-use Longman\TelegramBot\Entities\Message\MaybeInaccessibleMessage;
-use Longman\TelegramBot\Request;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class CallbackQuery.
- *
- * @link https://core.telegram.org/bots/api#callbackquery
- *
- * @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
+ * @method string                        getId()              Unique identifier for this query
+ * @method User                          getFrom()            Sender
+ * @method MaybeInaccessibleMessage|null getMessage()         Optional. Message sent by the bot with the callback button that originated the query
+ * @method string|null                   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|null                   getData()            Optional. Data associated with the callback button. Be aware that the message originated the query can contain no callback buttons with this data.
+ * @method string|null                   getGameShortName()   Optional. Short name of a Game to be returned, serves as the unique identifier for the game
  */
 class CallbackQuery extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'from'    => User::class,
-            'message' => MaybeInaccessibleMessageFactory::class,
+            'message' => MaybeInaccessibleMessage::class,
         ];
     }
-
-    /**
-     * Answer this callback query.
-     *
-     * @param array $data
-     *
-     * @return ServerResponse
-     */
-    public function answer(array $data = []): ServerResponse
-    {
-        return Request::answerCallbackQuery(array_merge([
-            'callback_query_id' => $this->getId(),
-        ], $data));
-    }
 }
diff --git a/src/Entities/ChannelPost.php b/src/Entities/ChannelPost.php
deleted file mode 100644
index fcc07afec..000000000
--- a/src/Entities/ChannelPost.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
-
-class ChannelPost extends Message
-{
-
-}
diff --git a/src/Entities/Chat.php b/src/Entities/Chat.php
index 8a46c519f..99c11c411 100644
--- a/src/Entities/Chat.php
+++ b/src/Entities/Chat.php
@@ -1,143 +1,32 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
-use Longman\TelegramBot\Entities\ReactionType\Factory as ReactionTypeFactory;
-use Longman\TelegramBot\Entities\ReactionType\ReactionType;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * This object represents a chat.
- *
- * @link https://core.telegram.org/bots/api#chat
- *
- * @property string type Type of chat, can be either "private ", "group", "supergroup" or "channel"
- *
- * @method int             getId()                                 Unique identifier for this chat. 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 string          getType()                               Type of chat, can be either "private ", "group", "supergroup" or "channel"
- * @method string          getTitle()                              Optional. Title, for channels and group chats
- * @method string          getUsername()                           Optional. Username, for private chats, supergroups and channels if available
- * @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.
- * @method bool            getHasPrivateForwards()                 Optional. True, if privacy settings of the other party in the private chat allows to use tg://user?id=<user_id> links only in chats with the user. Returned only in getChat.
- * @method bool            getHasRestrictedVoiceAndVideoMessages() Optional. True, if the privacy settings of the other party restrict sending voice and video note messages in the private chat. Returned only in getChat.
- * @method bool            getJoinToSendMessages()                 Optional. True, if users need to join the supergroup before they can send messages. Returned only in getChat.
- * @method bool            getJoinByRequest()                      Optional. True, if all users directly joining the supergroup need to be approved by supergroup administrators. Returned only in getChat.
- * @method string          getDescription()                        Optional. Description, for groups, supergroups and channel chats. Returned only in getChat.
- * @method string          getInviteLink()                         Optional. Chat invite link, for groups, supergroups and channel chats. Each administrator in a chat generates their own invite links, so the bot must first generate the link using exportChatInviteLink. Returned only in getChat.
- * @method Message         getPinnedMessage()                      Optional. Pinned message, for groups, supergroups and channels. Returned only in getChat.
- * @method ChatPermissions getPermissions()                        Optional. Default chat member permissions, for groups and supergroups. Returned only in getChat.
- * @method int             getSlowModeDelay()                      Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each unpriviledged user. Returned only in getChat.
- * @method int             getMessageAutoDeleteTime()              Optional. The time after which all messages sent to the chat will be automatically deleted; in seconds. Returned only in getChat.
- * @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.
- * @method ChatLocation    getLocation()                           Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat.
+ * @method int         getId()        Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method string      getType()      Type of the chat, can be either “private”, “group”, “supergroup” or “channel”
+ * @method string|null getTitle()     Optional. Title, for supergroups, channels and group chats
+ * @method string|null getUsername()  Optional. Username, for private chats, supergroups and channels if available
+ * @method string|null getFirstName() Optional. First name of the other party in a private chat
+ * @method string|null getLastName()  Optional. Last name of the other party in a private chat
+ * @method bool        isForum()      Optional. True, if the supergroup chat is a forum (has topics enabled).
  */
-class Chat extends Entity
+class Chat extends Entity implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'photo'               => ChatPhoto::class,
-            'available_reactions' => [ReactionTypeFactory::class],
-            'pinned_message'      => Message::class,
-            'permissions'         => ChatPermissions::class,
-            'location'            => ChatLocation::class,
-        ];
-    }
-
-    public function __construct(array $data)
-    {
-        parent::__construct($data);
+    public const TYPE_PRIVATE = 'private';
 
-        $id   = $this->getId();
-        $type = $this->getType();
-        if (!$type) {
-            $id > 0 && $this->type = 'private';
-            $id < 0 && $this->type = 'group';
-        }
-    }
+    public const TYPE_GROUP = 'group';
 
-    /**
-     * Try to mention the user of this chat, else return the title
-     *
-     * @param bool $escape_markdown
-     *
-     * @return string
-     */
-    public function tryMention($escape_markdown = false): string
-    {
-        if ($this->isPrivateChat()) {
-            return parent::tryMention($escape_markdown);
-        }
+    public const TYPE_SUPERGROUP = 'supergroup';
 
-        return $this->getTitle();
-    }
+    public const TYPE_CHANNEL = 'channel';
 
-    /**
-     * Check if this is a group chat
-     *
-     * @return bool
-     */
-    public function isGroupChat(): bool
+    public static function fieldsBypassingGet(): array
     {
-        return $this->getType() === 'group';
-    }
-
-    /**
-     * Check if this is a private chat
-     *
-     * @return bool
-     */
-    public function isPrivateChat(): bool
-    {
-        return $this->getType() === 'private';
-    }
-
-    /**
-     * Check if this is a super group
-     *
-     * @return bool
-     */
-    public function isSuperGroup(): bool
-    {
-        return $this->getType() === 'supergroup';
-    }
-
-    /**
-     * Check if this is a channel
-     *
-     * @return bool
-     */
-    public function isChannel(): bool
-    {
-        return $this->getType() === 'channel';
+        return [
+            'is_forum' => false,
+        ];
     }
 }
diff --git a/src/Entities/ChatAdministratorRights.php b/src/Entities/ChatAdministratorRights.php
index c6c50ff52..fd0ae8d2e 100644
--- a/src/Entities/ChatAdministratorRights.php
+++ b/src/Entities/ChatAdministratorRights.php
@@ -1,43 +1,46 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * Represents the rights of an administrator in a chat.
- *
- * @method bool getIsAnonymous()         True, if the user's presence in the chat is hidden
- * @method bool getCanManageChat()       True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege
- * @method bool getCanDeleteMessages()   True, if the administrator can delete messages of other users
- * @method bool getCanManageVideoChats() True, if the administrator can manage video chats
- * @method bool getCanRestrictMembers()  True, if the administrator can restrict, ban or unban chat members
- * @method bool getCanPromoteMembers()   True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user)
- * @method bool getCanChangeInfo()       True, if the user is allowed to change the chat title, photo and other settings
- * @method bool getCanInviteUsers()      True, if the user is allowed to invite new users to the chat
- * @method bool getCanPostMessages()     Optional. True, if the administrator can post in the channel; channels only
- * @method bool getCanEditMessages()     Optional. True, if the administrator can edit messages of other users and can pin messages; channels only
- * @method bool getCanPinMessages()      Optional. True, if the user is allowed to pin messages; groups and supergroups only
- * @method bool getCanPostStories()      Optional. True, if the administrator can post stories in the channel; channels only
- * @method bool getCanEditStories()      Optional. True, if the administrator can edit stories posted by other users; channels only
- * @method bool getCanDeleteStories()    Optional. True, if the administrator can delete stories posted by other users; channels only
- * @method bool getCanManageTopics()     Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only
- *
- * @method $this setIsAnonymous(bool $is_anonymous)                   True, if the user's presence in the chat is hidden
- * @method $this setCanManageChat(bool $can_manage_chat)              True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege
- * @method $this setCanDeleteMessages(bool $can_delete_messages)      True, if the administrator can delete messages of other users
- * @method $this setCanManageVideoChats(bool $can_manage_video_chats) True, if the administrator can manage video chats
- * @method $this setCanRestrictMembers(bool $can_restrict_members)    True, if the administrator can restrict, ban or unban chat members
- * @method $this setCanPromoteMembers(bool $can_promote_members)      True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user)
- * @method $this setCanChangeInfo(bool $can_change_info)              True, if the user is allowed to change the chat title, photo and other settings
- * @method $this setCanInviteUsers(bool $can_invite_users)            True, if the user is allowed to invite new users to the chat
- * @method $this setCanPostMessages(bool $can_post_messages)          Optional. True, if the administrator can post in the channel; channels only
- * @method $this setCanEditMessages(bool $can_edit_messages)          Optional. True, if the administrator can edit messages of other users and can pin messages; channels only
- * @method $this setCanPinMessages(bool $can_pin_messages)            Optional. True, if the user is allowed to pin messages; groups and supergroups only
- * @method $this setCanPostStories(bool $can_post_stories)            Optional. True, if the administrator can post stories in the channel; channels only
- * @method $this setCanEditStories(bool $can_edit_stories)            Optional. True, if the administrator can edit stories posted by other users; channels only
- * @method $this setCanDeleteStories(bool $can_delete_stories)        Optional. True, if the administrator can delete stories posted by other users; channels only
- * @method $this setCanManageTopics(bool $can_manage_topics)          Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only
+ * @method bool isAnonymous()         True, if the user's presence in the chat is hidden
+ * @method bool canManageChat()       True, if the administrator can access the chat event log, get boost list, see hidden supergroup and channel members, report spam messages and ignore slow mode. Implied by any other administrator privilege.
+ * @method bool canDeleteMessages()   True, if the administrator can delete messages of other users
+ * @method bool canManageVideoChats() True, if the administrator can manage video chats
+ * @method bool canRestrictMembers()  True, if the administrator can restrict, ban or unban chat members, or access supergroup statistics
+ * @method bool canPromoteMembers()   True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that they have promoted, directly or indirectly (promoted by administrators that were appointed by the user).
+ * @method bool canChangeInfo()       True, if the user is allowed to change the chat title, photo and other settings
+ * @method bool canInviteUsers()      True, if the user is allowed to invite new users to the chat
+ * @method bool canPostStories()      True, if the administrator can post stories to the chat
+ * @method bool canEditStories()      True, if the administrator can edit stories posted by other users, post stories to the chat page, pin chat stories, and access the chat's story archive
+ * @method bool canDeleteStories()    True, if the administrator can delete stories posted by other users
+ * @method bool canPostMessages()     Optional. True, if the administrator can post messages in the channel, or access channel statistics; for channels only
+ * @method bool canEditMessages()     Optional. True, if the administrator can edit messages of other users and can pin messages; for channels only
+ * @method bool canPinMessages()      Optional. True, if the user is allowed to pin messages; for groups and supergroups only
+ * @method bool canManageTopics()     Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only
  */
-class ChatAdministratorRights extends Entity
+class ChatAdministratorRights extends Entity implements AllowsBypassingGet
 {
-
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_anonymous'           => false,
+            'can_manage_chat'        => false,
+            'can_delete_messages'    => false,
+            'can_manage_video_chats' => false,
+            'can_restrict_members'   => false,
+            'can_promote_members'    => false,
+            'can_change_info'        => false,
+            'can_invite_users'       => false,
+            'can_post_stories'       => false,
+            'can_edit_stories'       => false,
+            'can_delete_stories'     => false,
+            'can_post_messages'      => false,
+            'can_edit_messages'      => false,
+            'can_pin_messages'       => false,
+            'can_manage_topics'      => false,
+        ];
+    }
 }
diff --git a/src/Entities/ChatBackground.php b/src/Entities/ChatBackground.php
new file mode 100644
index 000000000..f71412028
--- /dev/null
+++ b/src/Entities/ChatBackground.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Entities\BackgroundType\BackgroundType;
+
+/**
+ * @method BackgroundType getType() Type of the background
+ */
+class ChatBackground extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'type' => BackgroundType::class,
+        ];
+    }
+}
diff --git a/src/Entities/ChatBoost.php b/src/Entities/ChatBoost.php
index adfbddb03..7b7c980ad 100644
--- a/src/Entities/ChatBoost.php
+++ b/src/Entities/ChatBoost.php
@@ -1,37 +1,21 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
-use Longman\TelegramBot\Entities\ChatBoostSource\Factory as ChatBoostSourceFactory;
+use PhpTelegramBot\Core\Entities\ChatBoostSource\ChatBoostSource;
 
 /**
- * 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 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
+    protected static function subEntities(): array
     {
         return [
-            'source' => ChatBoostSourceFactory::class,
+            'source' => ChatBoostSource::class,
         ];
     }
 }
diff --git a/src/Entities/ChatBoostAdded.php b/src/Entities/ChatBoostAdded.php
new file mode 100644
index 000000000..04db2148c
--- /dev/null
+++ b/src/Entities/ChatBoostAdded.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int getBoostCount() Number of boosts added by the user
+ */
+class ChatBoostAdded
+{
+    //
+}
diff --git a/src/Entities/ChatBoostRemoved.php b/src/Entities/ChatBoostRemoved.php
index 2b893cca5..725c78f26 100644
--- a/src/Entities/ChatBoostRemoved.php
+++ b/src/Entities/ChatBoostRemoved.php
@@ -1,38 +1,22 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
-use Longman\TelegramBot\Entities\ChatBoostSource\Factory as ChatBoostSourceFactory;
+use PhpTelegramBot\Core\Entities\ChatBoostSource\ChatBoostSource;
 
 /**
- * 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 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
+    protected static function subEntities(): array
     {
         return [
             'chat'   => Chat::class,
-            'source' => ChatBoostSourceFactory::class,
+            'source' => ChatBoostSource::class,
         ];
     }
 }
diff --git a/src/Entities/ChatBoostSource/ChatBoostSource.php b/src/Entities/ChatBoostSource/ChatBoostSource.php
index dae048a90..4e21eaba4 100644
--- a/src/Entities/ChatBoostSource/ChatBoostSource.php
+++ b/src/Entities/ChatBoostSource/ChatBoostSource.php
@@ -1,16 +1,27 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatBoostSource;
+namespace PhpTelegramBot\Core\Entities\ChatBoostSource;
 
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
 
 /**
- * Interface ChatBoostSource
- *
  * @method string getSource() Source of the boost
- * @method User   getUser()   Information about the user
  */
-interface ChatBoostSource
+class ChatBoostSource extends Entity implements Factory
 {
-    //
+    public const SOURCE_PREMIUM = 'premium';
+
+    public const SOURCE_GIFT_CODE = 'gift_code';
+
+    public const SOURCE_GIVEAWAY = 'giveaway';
+
+    public static function make(array $data): static
+    {
+        return match ($data['source']) {
+            self::SOURCE_PREMIUM   => new ChatBoostSourcePremium($data),
+            self::SOURCE_GIFT_CODE => new ChatBoostSourceGiftCode($data),
+            self::SOURCE_GIVEAWAY  => new ChatBoostSourceGiveaway($data),
+        };
+    }
 }
diff --git a/src/Entities/ChatBoostSource/ChatBoostSourceGiftCode.php b/src/Entities/ChatBoostSource/ChatBoostSourceGiftCode.php
index 0228f71cd..f7802313d 100644
--- a/src/Entities/ChatBoostSource/ChatBoostSourceGiftCode.php
+++ b/src/Entities/ChatBoostSource/ChatBoostSourceGiftCode.php
@@ -1,24 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatBoostSource;
+namespace PhpTelegramBot\Core\Entities\ChatBoostSource;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * The boost was obtained by the creation of Telegram Premium gift codes to boost a chat.
- *
- * @link https://core.telegram.org/bots/api#chatboostsourcegiftcode
- *
- * @method string getSource() Source of the boost, always “gift_code”
- * @method User   getUser()   User for which the gift code was created
+ * @method User getUser() User for which the gift code was created
  */
-class ChatBoostSourceGiftCode extends Entity implements ChatBoostSource
+class ChatBoostSourceGiftCode extends ChatBoostSource
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
diff --git a/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php b/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php
index 7e4054801..2c98987a5 100644
--- a/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php
+++ b/src/Entities/ChatBoostSource/ChatBoostSourceGiveaway.php
@@ -1,29 +1,28 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatBoostSource;
+namespace PhpTelegramBot\Core\Entities\ChatBoostSource;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * The boost was obtained by the creation of a Telegram Premium giveaway. This boosts the chat 4 times for the duration of the corresponding Telegram Premium subscription.
- *
- * @link https://core.telegram.org/bots/api#chatboostsourcegiveaway
- *
- * @method string getSource()            Source of the boost, always “giveaway”
- * @method int    getGiveawayMessageId() Identifier of a message in the chat with the giveaway; the message could have been deleted already. May be 0 if the message isn't sent yet.
- * @method User   getUser()              Optional. User that won the prize in the giveaway if any
- * @method bool   getIsUnclaimed()       Optional. True, if the giveaway was completed, but there was no user to win the prize
+ * @method int       getGiveawayMessageId() Identifier of a message in the chat with the giveaway; the message could have been deleted already. May be 0 if the message isn't sent yet.
+ * @method User|null getUser()              Optional. User that won the prize in the giveaway if any
+ * @method bool      isUnclaimed()          Optional. True, if the giveaway was completed, but there was no user to win the prize
  */
-class ChatBoostSourceGiveaway extends Entity implements ChatBoostSource
+class ChatBoostSourceGiveaway extends ChatBoostSource implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_unclaimed' => false,
+        ];
+    }
 }
diff --git a/src/Entities/ChatBoostSource/ChatBoostSourceNotImplemented.php b/src/Entities/ChatBoostSource/ChatBoostSourceNotImplemented.php
deleted file mode 100644
index e045fcc08..000000000
--- a/src/Entities/ChatBoostSource/ChatBoostSourceNotImplemented.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\ChatBoostSource;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
-
-/**
- * @method string getSource() Source of the boost
- * @method User   getUser()   Information about the user
- */
-class ChatBoostSourceNotImplemented extends Entity implements ChatBoostSource
-{
-    //
-}
diff --git a/src/Entities/ChatBoostSource/ChatBoostSourcePremium.php b/src/Entities/ChatBoostSource/ChatBoostSourcePremium.php
index 5e76eb16a..1e1ec69e6 100644
--- a/src/Entities/ChatBoostSource/ChatBoostSourcePremium.php
+++ b/src/Entities/ChatBoostSource/ChatBoostSourcePremium.php
@@ -1,24 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatBoostSource;
+namespace PhpTelegramBot\Core\Entities\ChatBoostSource;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * The boost was obtained by subscribing to Telegram Premium or by gifting a Telegram Premium subscription to another user.
- *
- * @link https://core.telegram.org/bots/api#chatboostsourcepremium
- *
- * @method string getSource() Source of the boost, always “premium”
- * @method User   getUser()   User that boosted the chat
+ * @method User getUser() User that boosted the chat
  */
-class ChatBoostSourcePremium extends Entity implements ChatBoostSource
+class ChatBoostSourcePremium extends ChatBoostSource
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
diff --git a/src/Entities/ChatBoostSource/Factory.php b/src/Entities/ChatBoostSource/Factory.php
deleted file mode 100644
index 11e5af788..000000000
--- a/src/Entities/ChatBoostSource/Factory.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\ChatBoostSource;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class Factory extends \Longman\TelegramBot\Entities\Factory
-{
-    public static function make(array $data, string $bot_username): Entity
-    {
-        $type = [
-            'premium'   => 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
index 5228b9577..7ef5e65a5 100644
--- a/src/Entities/ChatBoostUpdated.php
+++ b/src/Entities/ChatBoostUpdated.php
@@ -1,34 +1,18 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\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
+    protected static function subEntities(): array
     {
         return [
-            'chat'       => Chat::class,
-            'chat_boost' => ChatBoost::class,
+            'chat'  => Chat::class,
+            'boost' => ChatBoost::class,
         ];
     }
 }
diff --git a/src/Entities/ChatFullInfo.php b/src/Entities/ChatFullInfo.php
new file mode 100644
index 000000000..47dc1423a
--- /dev/null
+++ b/src/Entities/ChatFullInfo.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\ReactionType\ReactionType;
+
+/**
+ * @method int                       getId()                              Unique identifier for this chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method string                    getType()                            Type of the chat, can be either “private”, “group”, “supergroup” or “channel”
+ * @method string|null               getTitle()                           Optional. Title, for supergroups, channels and group chats
+ * @method string|null               getUsername()                        Optional. Username, for private chats, supergroups and channels if available
+ * @method string|null               getFirstName()                       Optional. First name of the other party in a private chat
+ * @method string|null               getLastName()                        Optional. Last name of the other party in a private chat
+ * @method bool                      isForum()                            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 int                       getMaxReactionCount()                The maximum number of reactions that can be set on a message in the chat
+ * @method ChatPhoto|null            getPhoto()                           Optional. Chat photo
+ * @method string[]|null             getActiveUsernames()                 Optional. If non-empty, the list of all active chat usernames; for private chats, supergroups and channels
+ * @method Birthdate|null            getBirthdate()                       Optional. For private chats, the date of birth of the user
+ * @method BusinessIntro|null        getBusinessIntro()                   Optional. For private chats with business accounts, the intro of the business
+ * @method BusinessLocation|null     getBusinessLocation()                Optional. For private chats with business accounts, the location of the business
+ * @method BusinessOpeningHours|null getBusinessOpeningHours()            Optional. For private chats with business accounts, the opening hours of the business
+ * @method Chat|null                 getPersonalChat()                    Optional. For private chats, the personal channel of the user
+ * @method ReactionType[]|null       getAvailableReactions()              Optional. List of available reactions allowed in the chat. If omitted, then all emoji reactions are allowed.
+ * @method string|null               getBackgroundCustomEmojiId()         Optional. Custom emoji identifier of the emoji chosen by the chat for the reply header and link preview background
+ * @method int|null                  getProfileAccentColorId()            Optional. Identifier of the accent color for the chat's profile background. See profile accent colors for more details.
+ * @method string|null               getProfileBackgroundCustomEmojiId()  Optional. Custom emoji identifier of the emoji chosen by the chat for its profile background
+ * @method string|null               getEmojiStatusCustomEmojiId()        Optional. Custom emoji identifier of the emoji status of the chat or the other party in a private chat
+ * @method int|null                  getEmojiStatusExpirationDate()       Optional. Expiration date of the emoji status of the chat or the other party in a private chat, in Unix time, if any
+ * @method string|null               getBio()                             Optional. Bio of the other party in a private chat
+ * @method bool                      hasPrivateForwards()                 Optional. True, if privacy settings of the other party in the private chat allows to use tg://user?id=<user_id> links only in chats with the user
+ * @method bool                      hasRestrictedVoiceAndVideoMessages() Optional. True, if the privacy settings of the other party restrict sending voice and video note messages in the private chat
+ * @method true|null                 getJoinToSendMessages()              Optional. True, if users need to join the supergroup before they can send messages
+ * @method true|null                 getJoinByRequest()                   Optional. True, if all users directly joining the supergroup without using an invite link need to be approved by supergroup administrators
+ * @method string|null               getDescription()                     Optional. Description, for groups, supergroups and channel chats
+ * @method string|null               getInviteLink()                      Optional. Primary invite link, for groups, supergroups and channel chats
+ * @method Message|null              getPinnedMessage()                   Optional. The most recent pinned message (by sending date).
+ * @method ChatPermissions|null      getPermissions()                     Optional. Default chat member permissions, for groups and supergroups
+ * @method bool                      canSendPaidMedia()                   Optional. True, if paid media messages can be sent or forwarded to the channel chat. The field is available only for channel chats.
+ * @method int|null                  getSlowModeDelay()                   Optional. For supergroups, the minimum allowed delay between consecutive messages sent by each unprivileged user; in seconds
+ * @method int|null                  getUnrestrictBoostCount()            Optional. For supergroups, the minimum number of boosts that a non-administrator user needs to add in order to ignore slow mode and chat permissions
+ * @method int|null                  getMessageAutoDeleteTime()           Optional. The time after which all messages sent to the chat will be automatically deleted; in seconds
+ * @method bool                      hasAggressiveAntiSpamEnabled()       Optional. True, if aggressive anti-spam checks are enabled in the supergroup. The field is only available to chat administrators.
+ * @method bool                      hasHiddenMembers()                   Optional. True, if non-administrators can only get the list of bots and administrators in the chat
+ * @method bool                      hasProtectedContent()                Optional. True, if messages from the chat can't be forwarded to other chats
+ * @method bool                      hasVisibleHistory()                  Optional. True, if new chat members will have access to old messages; available only to chat administrators
+ * @method string|null               getStickerSetName()                  Optional. For supergroups, name of the group sticker set
+ * @method bool                      canSetStickerSet()                   Optional. True, if the bot can change the group sticker set
+ * @method string|null               getCustomEmojiStickerSetName()       Optional. For supergroups, the name of the group's custom emoji sticker set. Custom emoji from this set can be used by all users and bots in the group.
+ * @method int|null                  getLinkedChatId()                    Optional. Unique identifier for the linked chat, i.e. the discussion group identifier for a channel and vice versa; for supergroups and channel chats. This identifier may be greater than 32 bits and some programming languages may have difficulty/silent defects in interpreting it. But it is smaller than 52 bits, so a signed 64 bit integer or double-precision float type are safe for storing this identifier.
+ * @method ChatLocation|null         getLocation()                        Optional. For supergroups, the location to which the supergroup is connected
+ */
+class ChatFullInfo extends Entity implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'photo'                  => ChatPhoto::class,
+            'birthdate'              => Birthdate::class,
+            'business_intro'         => BusinessIntro::class,
+            'business_location'      => BusinessLocation::class,
+            'business_opening_hours' => BusinessOpeningHoursInterval::class,
+            'personal_chat'          => Chat::class,
+            'available_reactions'    => [ReactionType::class],
+            'pinned_message'         => Message::class,
+            'permissions'            => ChatPermissions::class,
+            'location'               => ChatLocation::class,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_forum'                                => false,
+            'has_private_forwards'                    => false,
+            'has_restricted_voice_and_video_messages' => false,
+            'can_send_paid_media'                     => false,
+            'has_aggressive_anti_spam_enabled'        => false,
+            'has_hidden_members'                      => false,
+            'has_protected_content'                   => false,
+            'has_visible_history'                     => false,
+            'can_set_sticker_set'                     => false,
+        ];
+    }
+}
diff --git a/src/Entities/ChatInviteLink.php b/src/Entities/ChatInviteLink.php
index 9f76567e5..a7691ed37 100644
--- a/src/Entities/ChatInviteLink.php
+++ b/src/Entities/ChatInviteLink.php
@@ -1,42 +1,34 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * Class ChatInviteLink
- *
- * Represents an invite link for a chat
- *
- * @link https://core.telegram.org/bots/api#chatinvitelink
- *
- * @method string  getInviteLink()              The invite link. If the link was created by another chat administrator, then the second part of the link will be replaced with “…”
- * @method User    getCreator()                 Creator of the link
- * @method bool    getCreatesJoinRequest()      True, if users joining the chat via the link need to be approved by chat administrators
- * @method bool    getIsPrimary()               True, if the link is primary
- * @method bool    getIsRevoked()               True, if the link is revoked
- * @method string  getName()                    Optional. Invite link name
- * @method int     getExpireDate()              Optional. Point in time (Unix timestamp) when the link will expire or has been expired
- * @method int     getMemberLimit()             Optional. Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999
- * @method int     getPendingJoinRequestCount() Optional. Number of pending join requests created using this link
+ * @method string      getInviteLink()              The invite link. If the link was created by another chat administrator, then the second part of the link will be replaced with “…”.
+ * @method User        getCreator()                 Creator of the link
+ * @method bool        getCreatesJoinRequest()      True, if users joining the chat via the link need to be approved by chat administrators
+ * @method bool        isPrimary()                  True, if the link is primary
+ * @method bool        isRevoked()                  True, if the link is revoked
+ * @method string|null getName()                    Optional. Invite link name
+ * @method int|null    getExpireDate()              Optional. Point in time (Unix timestamp), when the link will expire or has been expired
+ * @method int|null    getMemberLimit()             Optional. The maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999
+ * @method int|null    getPendingJoinRequestCount() Optional. Number of pending join requests created using this link
  */
-class ChatInviteLink extends Entity
+class ChatInviteLink extends Entity implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'creator' => User::class,
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_primary' => false,
+            'is_revoked' => false,
+        ];
+    }
 }
diff --git a/src/Entities/ChatJoinRequest.php b/src/Entities/ChatJoinRequest.php
index 5d2c3433a..d91251547 100644
--- a/src/Entities/ChatJoinRequest.php
+++ b/src/Entities/ChatJoinRequest.php
@@ -1,24 +1,18 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class ChatJoinRequest
- *
- * Represents a join request sent to a chat.
- *
- * @link https://core.telegram.org/bots/api#chatjoinrequest
- *
- * @method Chat           getChat()       Chat to which the request was sent
- * @method User           getFrom()       User that sent the join request
- * @method int            getUserChatId() Identifier of a private chat with the user who sent the join request.
- * @method int            getDate()       Date the request was sent in Unix time
- * @method string         getBio()        Optional. Bio of the user.
- * @method ChatInviteLink getInviteLink() Optional. Chat invite link that was used by the user to send the join request
+ * @method Chat                getChat()       Chat to which the request was sent
+ * @method User                getFrom()       User that sent the join request
+ * @method int                 getUserChatId() Identifier of a private chat with the user who sent the join request. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. The bot can use this identifier for 5 minutes to send messages until the join request is processed, assuming no other administrator contacted the user.
+ * @method int                 getDate()       Date the request was sent in Unix time
+ * @method string|null         getBio()        Optional. Bio of the user.
+ * @method ChatInviteLink|null getInviteLink() Optional. Chat invite link that was used by the user to send the join request
  */
 class ChatJoinRequest extends Entity
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'chat'        => Chat::class,
diff --git a/src/Entities/ChatLocation.php b/src/Entities/ChatLocation.php
index 2f5433dd2..4d02b421c 100644
--- a/src/Entities/ChatLocation.php
+++ b/src/Entities/ChatLocation.php
@@ -1,32 +1,14 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class ChatLocation
- *
- * Represents a location to which a chat is connected.
- *
- * @link https://core.telegram.org/bots/api#chatlocation
- *
  * @method Location getLocation() The location to which the supergroup is connected. Can't be a live location.
  * @method string   getAddress()  Location address; 1-64 characters, as defined by the chat owner
  */
 class ChatLocation extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'location' => Location::class,
diff --git a/src/Entities/ChatMember/ChatMember.php b/src/Entities/ChatMember/ChatMember.php
index 125a6fa63..d5891eeda 100644
--- a/src/Entities/ChatMember/ChatMember.php
+++ b/src/Entities/ChatMember/ChatMember.php
@@ -1,16 +1,36 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
 
 /**
- * Interface ChatMember
- *
  * @method string getStatus() The member's status in the chat
- * @method User   getUser()   Information about the user
  */
-interface ChatMember
+class ChatMember extends Entity implements Factory
 {
-    //
+    public const TYPE_CREATOR = 'creator';
+
+    public const TYPE_ADMINISTRATOR = 'administrator';
+
+    public const TYPE_MEMBER = 'member';
+
+    public const TYPE_RESTRICTED = 'restricted';
+
+    public const TYPE_LEFT = 'left';
+
+    public const TYPE_KICKED = 'kicked';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_CREATOR       => new ChatMemberOwner($data),
+            self::TYPE_ADMINISTRATOR => new ChatMemberAdministrator($data),
+            self::TYPE_MEMBER        => new ChatMemberMember($data),
+            self::TYPE_RESTRICTED    => new ChatMemberRestricted($data),
+            self::TYPE_LEFT          => new ChatMemberLeft($data),
+            self::TYPE_KICKED        => new ChatMemberBanned($data),
+        };
+    }
 }
diff --git a/src/Entities/ChatMember/ChatMemberAdministrator.php b/src/Entities/ChatMember/ChatMemberAdministrator.php
index 46a9cb356..96b30803a 100644
--- a/src/Entities/ChatMember/ChatMemberAdministrator.php
+++ b/src/Entities/ChatMember/ChatMemberAdministrator.php
@@ -1,44 +1,53 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * Class ChatMemberAdministrator
- *
- * @link https://core.telegram.org/bots/api#chatmemberadministrator
- *
- * @method string getStatus()              The member's status in the chat, always “administrator”
- * @method User   getUser()                Information about the user
- * @method bool   getCanBeEdited()         True, if the bot is allowed to edit administrator privileges of that user
- * @method bool   getIsAnonymous()         True, if the user's presence in the chat is hidden
- * @method bool   getCanManageChat()       True, if the administrator can access the chat event log, chat statistics, message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege
- * @method bool   getCanDeleteMessages()   True, if the administrator can delete messages of other users
- * @method bool   getCanManageVideoChats() True, if the administrator can manage video chats
- * @method bool   getCanRestrictMembers()  True, if the administrator can restrict, ban or unban chat members
- * @method bool   getCanPromoteMembers()   True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that he has promoted, directly or indirectly (promoted by administrators that were appointed by the user)
- * @method bool   getCanChangeInfo()       True, if the user is allowed to change the chat title, photo and other settings
- * @method bool   getCanInviteUsers()      True, if the user is allowed to invite new users to the chat
- * @method bool   getCanPostMessages()     Optional. True, if the administrator can post in the channel; channels only
- * @method bool   getCanEditMessages()     Optional. True, if the administrator can edit messages of other users and can pin messages; channels only
- * @method bool   getCanPinMessages()      Optional. True, if the user is allowed to pin messages; groups and supergroups only
- * @method bool   getCanPostStories()      Optional. True, if the administrator can post stories in the channel; channels only
- * @method bool   getCanEditStories()      Optional. True, if the administrator can edit stories posted by other users; channels only
- * @method bool   getCanDeleteStories()    Optional. True, if the administrator can delete stories posted by other users; channels only
- * @method bool   getCanManageTopics()     Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; supergroups only
- * @method string getCustomTitle()         Custom title for this user
+ * @method User        getUser()             Information about the user
+ * @method bool        canBeEdited()         True, if the bot is allowed to edit administrator privileges of that user
+ * @method bool        isAnonymous()         True, if the user's presence in the chat is hidden
+ * @method bool        canManageChat()       True, if the administrator can access the chat event log, get boost list, see hidden supergroup and channel members, report spam messages and ignore slow mode. Implied by any other administrator privilege.
+ * @method bool        canDeleteMessages()   True, if the administrator can delete messages of other users
+ * @method bool        canManageVideoChats() True, if the administrator can manage video chats
+ * @method bool        canRestrictMembers()  True, if the administrator can restrict, ban or unban chat members, or access supergroup statistics
+ * @method bool        canPromoteMembers()   True, if the administrator can add new administrators with a subset of their own privileges or demote administrators that they have promoted, directly or indirectly (promoted by administrators that were appointed by the user).
+ * @method bool        canChangeInfo()       True, if the user is allowed to change the chat title, photo and other settings
+ * @method bool        canInviteUsers()      True, if the user is allowed to invite new users to the chat
+ * @method bool        canPostStories()      True, if the administrator can post stories to the chat
+ * @method bool        canEditStories()      True, if the administrator can edit stories posted by other users, post stories to the chat page, pin chat stories, and access the chat's story archive
+ * @method bool        canDeleteStories()    True, if the administrator can delete stories posted by other users
+ * @method bool        canPostMessages()     Optional. True, if the administrator can post messages in the channel, or access channel statistics; for channels only
+ * @method bool        canEditMessages()     Optional. True, if the administrator can edit messages of other users and can pin messages; for channels only
+ * @method bool        canPinMessages()      Optional. True, if the user is allowed to pin messages; for groups and supergroups only
+ * @method bool        canManageTopics()     Optional. True, if the user is allowed to create, rename, close, and reopen forum topics; for supergroups only
+ * @method string|null getCustomTitle()      Optional. Custom title for this user
  */
-class ChatMemberAdministrator extends Entity implements ChatMember
+class ChatMemberAdministrator extends ChatMember implements AllowsBypassingGet
 {
-    /**
-     * @inheritDoc
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'can_be_edited'          => false,
+            'is_anonymous'           => false,
+            'can_manage_chat'        => false,
+            'can_delete_messages'    => false,
+            'can_manage_video_chats' => false,
+            'can_restrict_members'   => false,
+            'can_promote_members'    => false,
+            'can_change_info'        => false,
+            'can_invite_users'       => false,
+            'can_post_stories'       => false,
+            'can_edit_stories'       => false,
+        ];
+    }
 }
diff --git a/src/Entities/ChatMember/ChatMemberBanned.php b/src/Entities/ChatMember/ChatMemberBanned.php
index ed824cd33..280c4cf81 100644
--- a/src/Entities/ChatMember/ChatMemberBanned.php
+++ b/src/Entities/ChatMember/ChatMemberBanned.php
@@ -1,25 +1,17 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Entities\Entity;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * Class ChatMemberBanned
- *
- * @link https://core.telegram.org/bots/api#chatmemberbanned
- *
- * @method string getStatus()    The member's status in the chat, always “kicked”
- * @method User   getUser()      Information about the user
- * @method int    getUntilDate() Date when restrictions will be lifted for this user; unix time
+ * @method User getUser()      Information about the user
+ * @method int  getUntilDate() Date when restrictions will be lifted for this user; Unix time. If 0, then the user is banned forever
  */
-class ChatMemberBanned extends Entity implements ChatMember
+class ChatMemberBanned extends Entity
 {
-    /**
-     * @inheritDoc
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
diff --git a/src/Entities/ChatMember/ChatMemberLeft.php b/src/Entities/ChatMember/ChatMemberLeft.php
index 495fb2bc6..1b45f92c8 100644
--- a/src/Entities/ChatMember/ChatMemberLeft.php
+++ b/src/Entities/ChatMember/ChatMemberLeft.php
@@ -1,24 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * Class ChatMemberLeft
- *
- * @link https://core.telegram.org/bots/api#chatmemberleft
- *
- * @method string getStatus() The member's status in the chat, always “left”
- * @method User   getUser()   Information about the user
+ * @method User getUser() Information about the user
  */
-class ChatMemberLeft extends Entity implements ChatMember
+class ChatMemberLeft extends ChatMember
 {
-    /**
-     * @inheritDoc
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
diff --git a/src/Entities/ChatMember/ChatMemberMember.php b/src/Entities/ChatMember/ChatMemberMember.php
index 15c82a9cf..eb14c8657 100644
--- a/src/Entities/ChatMember/ChatMemberMember.php
+++ b/src/Entities/ChatMember/ChatMemberMember.php
@@ -1,24 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * Class ChatMemberMember
- *
- * @link https://core.telegram.org/bots/api#chatmembermember
- *
- * @method string getStatus() The member's status in the chat, always “member”
- * @method User   getUser()   Information about the user
+ * @method User getUser() Information about the user
  */
-class ChatMemberMember extends Entity implements ChatMember
+class ChatMemberMember extends ChatMember
 {
-    /**
-     * @inheritDoc
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
diff --git a/src/Entities/ChatMember/ChatMemberNotImplemented.php b/src/Entities/ChatMember/ChatMemberNotImplemented.php
deleted file mode 100644
index 0b3230e17..000000000
--- a/src/Entities/ChatMember/ChatMemberNotImplemented.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\ChatMember;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
-
-/**
- * Class ChatMemberNotImplemented
- *
- * @method string getStatus() The member's status in the chat
- * @method User   getUser()   Information about the user
- */
-class ChatMemberNotImplemented extends Entity implements ChatMember
-{
-    //
-}
diff --git a/src/Entities/ChatMember/ChatMemberOwner.php b/src/Entities/ChatMember/ChatMemberOwner.php
index 9be4b13ef..9b8bb88f8 100644
--- a/src/Entities/ChatMember/ChatMemberOwner.php
+++ b/src/Entities/ChatMember/ChatMemberOwner.php
@@ -1,29 +1,28 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * Class ChatMemberOwner
- *
- * @link https://core.telegram.org/bots/api#chatmemberowner
- *
- * @method string getStatus()      The member's status in the chat, always “creator”
- * @method User   getUser()        Information about the user
- * @method string getCustomTitle() Custom title for this user
- * @method bool   getIsAnonymous() True, if the user's presence in the chat is hidden
+ * @method User        getUser()        Information about the user
+ * @method bool        isAnonymous()    True, if the user's presence in the chat is hidden
+ * @method string|null getCustomTitle() Optional. Custom title for this user
  */
-class ChatMemberOwner extends Entity implements ChatMember
+class ChatMemberOwner extends ChatMember implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_anonymous' => false,
+        ];
+    }
 }
diff --git a/src/Entities/ChatMember/ChatMemberRestricted.php b/src/Entities/ChatMember/ChatMemberRestricted.php
index d4f36b7b3..713446356 100644
--- a/src/Entities/ChatMember/ChatMemberRestricted.php
+++ b/src/Entities/ChatMember/ChatMemberRestricted.php
@@ -1,60 +1,52 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ChatMember;
+namespace PhpTelegramBot\Core\Entities\ChatMember;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * Class ChatMemberRestricted
- *
- * @link https://core.telegram.org/bots/api#chatmemberrestricted
- *
- * @method string getStatus()                The member's status in the chat, always “restricted”
- * @method User   getUser()                  Information about the user
- * @method bool   getIsMember()              True, if the user is a member of the chat at the moment of the request
- * @method bool   getCanChangeInfo()         True, if the user is allowed to change the chat title, photo and other settings
- * @method bool   getCanInviteUsers()        True, if the user is allowed to invite new users to the chat
- * @method bool   getCanPinMessages()        True, if the user is allowed to pin messages; groups and supergroups only
- * @method bool   getCanManageTopics()       True, if the user is allowed to create forum topics
- * @method bool   getCanSendMessages()       True, if the user is allowed to send text messages, contacts, locations and venues
- * @method bool   getCanSendAudios()         True, if the user is allowed to send audios
- * @method bool   getCanSendDocuments()      True, if the user is allowed to send documents
- * @method bool   getCanSendPhotos()         True, if the user is allowed to send photos
- * @method bool   getCanSendVideos()         True, if the user is allowed to send videos
- * @method bool   getCanSendVideoNotes()     True, if the user is allowed to send video notes
- * @method bool   getCanSendVoiceNotes()     True, if the user is allowed to send voice notes
- * @method bool   getCanSendPolls()          True, if the user is allowed to send polls
- * @method bool   getCanSendOtherMessages()  True, if the user is allowed to send animations, games, stickers and use inline bots
- * @method bool   getCanAddWebPagePreviews() True, if the user is allowed to add web page previews to their messages
- * @method int    getUntilDate()             Date when restrictions will be lifted for this user; unix time
+ * @method User getUser()               Information about the user
+ * @method bool isMember()              True, if the user is a member of the chat at the moment of the request
+ * @method bool canSendMessages()       True, if the user is allowed to send text messages, contacts, giveaways, giveaway winners, invoices, locations and venues
+ * @method bool canSendAudios()         True, if the user is allowed to send audios
+ * @method bool canSendDocuments()      True, if the user is allowed to send documents
+ * @method bool canSendPhotos()         True, if the user is allowed to send photos
+ * @method bool canSendVideos()         True, if the user is allowed to send videos
+ * @method bool canSendVideoNotes()     True, if the user is allowed to send video notes
+ * @method bool canSendVoiceNotes()     True, if the user is allowed to send voice notes
+ * @method bool canSendPolls()          True, if the user is allowed to send polls
+ * @method bool canSendOtherMessages()  True, if the user is allowed to send animations, games, stickers and use inline bots
+ * @method bool canAddWebPagePreviews() True, if the user is allowed to add web page previews to their messages
+ * @method bool canChangeInfo()         True, if the user is allowed to change the chat title, photo and other settings
+ * @method bool canInviteUsers()        True, if the user is allowed to invite new users to the chat
+ * @method bool canPinMessages()        True, if the user is allowed to pin messages
+ * @method bool canManageTopics()       True, if the user is allowed to create forum topics
+ * @method int  getUntilDate()          Date when restrictions will be lifted for this user; Unix time. If 0, then the user is restricted forever
  */
-class ChatMemberRestricted extends Entity implements ChatMember
+class ChatMemberRestricted extends ChatMember implements AllowsBypassingGet
 {
-    /**
-     * @inheritDoc
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
         ];
     }
 
-    /**
-     * True, if the user is allowed to send audios, documents, photos, videos, video notes OR voice notes
-     *
-     * @deprecated Use new fine-grained methods provided by Telegram Bot API.
-     *
-     * @return bool
-     */
-    public function getCanSendMediaMessages(): bool
+    public static function fieldsBypassingGet(): array
     {
-        return $this->getCanSendAudios() ||
-            $this->getCanSendDocuments() ||
-            $this->getCanSendPhotos() ||
-            $this->getCanSendVideos() ||
-            $this->getCanSendVideoNotes() ||
-            $this->getCanSendVoiceNotes();
+        return [
+            'is_member'                 => false,
+            'can_send_messages'         => false,
+            'can_send_audios'           => false,
+            'can_send_documents'        => false,
+            'can_send_photos'           => false,
+            'can_send_videos'           => false,
+            'can_send_video_notes'      => false,
+            'can_send_voice_notes'      => false,
+            'can_send_polls'            => false,
+            'can_send_other_messages'   => false,
+            'can_add_web_page_previews' => false,
+        ];
     }
 }
diff --git a/src/Entities/ChatMember/Factory.php b/src/Entities/ChatMember/Factory.php
deleted file mode 100644
index 4ffe5d315..000000000
--- a/src/Entities/ChatMember/Factory.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\ChatMember;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class Factory extends \Longman\TelegramBot\Entities\Factory
-{
-    public static function make(array $data, string $bot_username): Entity
-    {
-        $type = [
-            'creator'       => ChatMemberOwner::class,
-            'administrator' => ChatMemberAdministrator::class,
-            'member'        => ChatMemberMember::class,
-            'restricted'    => ChatMemberRestricted::class,
-            'left'          => ChatMemberLeft::class,
-            'kicked'        => ChatMemberBanned::class,
-        ];
-
-        if (!isset($type[$data['status'] ?? ''])) {
-            return new ChatMemberNotImplemented($data, $bot_username);
-        }
-
-        $class = $type[$data['status']];
-        return new $class($data, $bot_username);
-    }
-}
diff --git a/src/Entities/ChatMemberUpdated.php b/src/Entities/ChatMemberUpdated.php
index 172ddfb0f..e6b1cbbf9 100644
--- a/src/Entities/ChatMemberUpdated.php
+++ b/src/Entities/ChatMemberUpdated.php
@@ -1,46 +1,28 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
-use Longman\TelegramBot\Entities\ChatMember\ChatMember;
-use Longman\TelegramBot\Entities\ChatMember\Factory as ChatMemberFactory;
+use PhpTelegramBot\Core\Entities\ChatMember\ChatMember;
 
 /**
- * Class ChatMemberUpdated
- *
- * Represents changes in the status of a chat member
- *
- * @link https://core.telegram.org/bots/api#chatmemberupdated
- *
- * @method Chat            getChat()              Chat the user belongs to
- * @method User            getFrom()              Performer of the action, which resulted in the change
- * @method int             getDate()              Date the change was done in Unix time
- * @method ChatMember      getOldChatMember()     Previous information about the chat member
- * @method ChatMember      getNewChatMember()     New information about the chat member
- * @method ChatInviteLink  getInviteLink()        Optional. Chat invite link, which was used by the user to join the chat; for joining by invite link events only.
- * @method bool            getViaChatFolderInviteLink() Optional. True, if the user joined the chat via a chat folder invite link
+ * @method Chat                getChat()                    Chat the user belongs to
+ * @method User                getFrom()                    Performer of the action, which resulted in the change
+ * @method int                 getDate()                    Date the change was done in Unix time
+ * @method ChatMember          getOldChatMember()           Previous information about the chat member
+ * @method ChatMember          getNewChatMember()           New information about the chat member
+ * @method ChatInviteLink|null getInviteLink()              Optional. Chat invite link, which was used by the user to join the chat; for joining by invite link events only.
+ * @method bool|null           getViaJoinRequest()          Optional. True, if the user joined the chat after sending a direct join request without using an invite link and being approved by an administrator
+ * @method bool|null           getViaChatFolderInviteLink() Optional. True, if the user joined the chat via a chat folder invite link
  */
 class ChatMemberUpdated extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'chat'            => Chat::class,
             'from'            => User::class,
-            'old_chat_member' => ChatMemberFactory::class,
-            'new_chat_member' => ChatMemberFactory::class,
+            'old_chat_member' => ChatMember::class,
+            'new_chat_member' => ChatMember::class,
             'invite_link'     => ChatInviteLink::class,
         ];
     }
diff --git a/src/Entities/ChatPermissions.php b/src/Entities/ChatPermissions.php
index 504ccea44..4542918aa 100644
--- a/src/Entities/ChatPermissions.php
+++ b/src/Entities/ChatPermissions.php
@@ -1,52 +1,42 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * Class ChatPermissions
- *
- * @link https://core.telegram.org/bots/api#chatpermissions
- *
- * @method bool getCanSendMessages()       Optional. True, if the user is allowed to send text messages, contacts, locations and venues
- * @method bool getCanSendAudios()         Optional. True, if the user is allowed to send audios
- * @method bool getCanSendDocuments()      Optional. True, if the user is allowed to send documents
- * @method bool getCanSendPhotos()         Optional. True, if the user is allowed to send photos
- * @method bool getCanSendVideos()         Optional. True, if the user is allowed to send videos
- * @method bool getCanSendVideoNotes()     Optional. True, if the user is allowed to send video notes
- * @method bool getCanSendVoiceNotes()     Optional. True, if the user is allowed to send voice notes
- * @method bool getCanSendPolls()          Optional. True, if the user is allowed to send polls, implies can_send_messages
- * @method bool getCanSendOtherMessages()  Optional. True, if the user is allowed to send animations, games, stickers and use inline bots, implies can_send_media_messages
- * @method bool getCanAddWebPagePreviews() Optional. True, if the user is allowed to add web page previews to their messages, implies can_send_media_messages
- * @method bool getCanChangeInfo()         Optional. True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups
- * @method bool getCanInviteUsers()        Optional. True, if the user is allowed to invite new users to the chat
- * @method bool getCanPinMessages()        Optional. True, if the user is allowed to pin messages. Ignored in public supergroups
- * @method bool getCanManageTopics()       Optional. True, if the user is allowed to create forum topics. If omitted defaults to the value of can_pin_messages
+ * @method bool canSendMessages()       Optional. True, if the user is allowed to send text messages, contacts, giveaways, giveaway winners, invoices, locations and venues
+ * @method bool canSendAudios()         Optional. True, if the user is allowed to send audios
+ * @method bool canSendDocuments()      Optional. True, if the user is allowed to send documents
+ * @method bool canSendPhotos()         Optional. True, if the user is allowed to send photos
+ * @method bool canSendVideos()         Optional. True, if the user is allowed to send videos
+ * @method bool canSendVideoNotes()     Optional. True, if the user is allowed to send video notes
+ * @method bool canSendVoiceNotes()     Optional. True, if the user is allowed to send voice notes
+ * @method bool canSendPolls()          Optional. True, if the user is allowed to send polls
+ * @method bool canSendOtherMessages()  Optional. True, if the user is allowed to send animations, games, stickers and use inline bots
+ * @method bool canAddWebPagePreviews() Optional. True, if the user is allowed to add web page previews to their messages
+ * @method bool canChangeInfo()         Optional. True, if the user is allowed to change the chat title, photo and other settings. Ignored in public supergroups
+ * @method bool canInviteUsers()        Optional. True, if the user is allowed to invite new users to the chat
+ * @method bool canPinMessages()        Optional. True, if the user is allowed to pin messages. Ignored in public supergroups
+ * @method bool canManageTopics()       Optional. True, if the user is allowed to create forum topics. If omitted defaults to the value of can_pin_message
  */
-class ChatPermissions extends Entity
+class ChatPermissions extends Entity implements AllowsBypassingGet
 {
-    /**
-     * True, if the user is allowed to send audios, documents, photos, videos, video notes OR voice notes
-     *
-     * @deprecated Use new fine-grained methods provided by Telegram Bot API.
-     *
-     * @return bool
-     */
-    public function getCanSendMediaMessages(): bool
+    //
+    public static function fieldsBypassingGet(): array
     {
-        return $this->getCanSendAudios() ||
-            $this->getCanSendDocuments() ||
-            $this->getCanSendPhotos() ||
-            $this->getCanSendVideos() ||
-            $this->getCanSendVideoNotes() ||
-            $this->getCanSendVoiceNotes();
+        return [
+            'can_send_messages'         => false,
+            'can_send_audios'           => false,
+            'can_send_documents'        => false,
+            'can_send_photos'           => false,
+            'can_send_videos'           => false,
+            'can_send_video_notes'      => false,
+            'can_send_voice_notes'      => false,
+            'can_send_polls'            => false,
+            'can_send_other_messages'   => false,
+            'can_add_web_page_previews' => false,
+            'can_change_info'           => false,
+        ];
     }
 }
diff --git a/src/Entities/ChatPhoto.php b/src/Entities/ChatPhoto.php
index eb5d54a8a..4c2ea3f94 100644
--- a/src/Entities/ChatPhoto.php
+++ b/src/Entities/ChatPhoto.php
@@ -1,27 +1,14 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class ChatPhoto
- *
- * @link https://core.telegram.org/bots/api#chatphoto
- *
- * @method string getSmallFileId()       File identifier of small (160x160) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed.
- * @method string getSmallFileUniqueId() Unique file identifier of small (160x160) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method string getBigFileId()         File identifier of big (640x640) chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed.
- * @method string getBigFileUniqueId()   Unique file identifier of big (640x640) chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method string getSmallFileId()       File identifier of small (160x160), chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed.
+ * @method string getSmallFileUniqueId() Unique file identifier of small (160x160), chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method string getBigFileId()         File identifier of big (640x640), chat photo. This file_id can be used only for photo download and only for as long as the photo is not changed.
+ * @method string getBigFileUniqueId()   Unique file identifier of big (640x640), chat photo, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
  */
 class ChatPhoto extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/ChatShared.php b/src/Entities/ChatShared.php
index 5987d36db..a43a78769 100644
--- a/src/Entities/ChatShared.php
+++ b/src/Entities/ChatShared.php
@@ -1,18 +1,20 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class ChatShared
- *
- * This object contains information about the chat whose identifier was shared with the bot using a KeyboardButtonRequestChat button.
- *
- * @link https://core.telegram.org/bots/api#chatshared
- *
- * @method int getRequestId() Identifier of the request
- * @method int getChatId()    Identifier of the shared chat.
+ * @method int              getRequestId() Identifier of the request
+ * @method int              getChatId()    Identifier of the shared chat. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier. The bot may not have access to the chat and could be unable to use this identifier, unless the chat is already known to the bot by some other means.
+ * @method string|null      getTitle()     Optional. Title of the chat, if the title was requested by the bot.
+ * @method string|null      getUsername()  Optional. Username of the chat, if the username was requested by the bot and available.
+ * @method PhotoSize[]|null getPhoto()     Optional. Available sizes of the chat photo, if the photo was requested by the bot
  */
 class ChatShared extends Entity
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'photo' => [PhotoSize::class],
+        ];
+    }
 }
diff --git a/src/Entities/ChosenInlineResult.php b/src/Entities/ChosenInlineResult.php
index 325d7cf63..156cc6c7b 100644
--- a/src/Entities/ChosenInlineResult.php
+++ b/src/Entities/ChosenInlineResult.php
@@ -1,33 +1,17 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class ChosenInlineResult
- *
- * @link https://core.telegram.org/bots/api#choseninlineresult
- *
- * @method string   getResultId()        The unique identifier for the result that was chosen
- * @method User     getFrom()            The user that chose the result
- * @method Location getLocation()        Optional. Sender location, only for bots that require user location
- * @method string   getInlineMessageId() Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message.
- * @method string   getQuery()           The query that was used to obtain the result
+ * @method string        getResultId()        The unique identifier for the result that was chosen
+ * @method User          getFrom()            The user that chose the result
+ * @method Location|null getLocation()        Optional. Sender location, only for bots that require user location
+ * @method string|null   getInlineMessageId() Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message. Will be also received in callback queries and can be used to edit the message.
+ * @method string        getQuery()           The query that was used to obtain the result
  */
 class ChosenInlineResult extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'from'     => User::class,
diff --git a/src/Entities/Contact.php b/src/Entities/Contact.php
index d51056d83..b1f19d796 100644
--- a/src/Entities/Contact.php
+++ b/src/Entities/Contact.php
@@ -1,28 +1,15 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Contact
- *
- * @link https://core.telegram.org/bots/api#contact
- *
- * @method string getPhoneNumber() Contact's phone number
- * @method string getFirstName()   Contact's first name
- * @method string getLastName()    Optional. Contact's last name
- * @method int    getUserId()      Optional. Contact's user identifier in Telegram
- * @method string getVcard()       Optional. Additional data about the contact in the form of a vCard
+ * @method string      getPhoneNumber() Contact's phone number
+ * @method string      getFirstName()   Contact's first name
+ * @method string|null getLastName()    Optional. Contact's last name
+ * @method int|null    getUserId()      Optional. Contact's user identifier in Telegram. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method string|null getVcard()       Optional. Additional data about the contact in the form of a vCard
  */
 class Contact extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/Dice.php b/src/Entities/Dice.php
index 52ee5870d..fabe26fe6 100644
--- a/src/Entities/Dice.php
+++ b/src/Entities/Dice.php
@@ -1,27 +1,22 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Dice
- *
- * This entity represents a dice with random value from 1 to 6.
- *
- * @link https://core.telegram.org/bots/api#dice
- *
  * @method string getEmoji() Emoji on which the dice throw animation is based
- * @method int    getValue() Value of the dice, 1-6 for “🎲” and “🎯” base emoji, 1-5 for “🏀” and “⚽” base emoji, 1-64 for “🎰” base emoji
+ * @method int    getValue() Value of the dice, 1-6 for “🎲”, “🎯” and “🎳” base emoji, 1-5 for “🏀” and “⚽” base emoji, 1-64 for “🎰” base emoji
  */
 class Dice extends Entity
 {
+    public const EMOJI_DICE = '🎲';
+
+    public const EMOJI_DARTS = '🎯';
+
+    public const EMOJI_BOWLING = '🎳';
+
+    public const EMOJI_BASKETBALL = '🏀';
+
+    public const EMOJI_SOCCER = '⚽';
 
+    public const EMOJI_SLOT_MACHINE = '🎰';
 }
diff --git a/src/Entities/Document.php b/src/Entities/Document.php
index aff0b7d93..e0f0a9d24 100644
--- a/src/Entities/Document.php
+++ b/src/Entities/Document.php
@@ -1,34 +1,18 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Document
- *
- * @link https://core.telegram.org/bots/api#document
- *
- * @method string    getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string    getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method PhotoSize getThumbnail()    Optional. Document thumbnail as defined by sender
- * @method string    getFileName()     Optional. Original filename as defined by sender
- * @method string    getMimeType()     Optional. MIME type of the file as defined by sender
- * @method int       getFileSize()     Optional. File size
+ * @method string         getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string         getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method PhotoSize|null getThumbnail()    Optional. Document thumbnail as defined by sender
+ * @method string|null    getFileName()     Optional. Original filename as defined by sender
+ * @method string|null    getMimeType()     Optional. MIME type of the file as defined by sender
+ * @method int|null       getFileSize()     Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.
  */
 class Document extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'thumbnail' => PhotoSize::class,
diff --git a/src/Entities/EditedChannelPost.php b/src/Entities/EditedChannelPost.php
deleted file mode 100644
index 66036b2c5..000000000
--- a/src/Entities/EditedChannelPost.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
-
-class EditedChannelPost extends Message
-{
-
-}
diff --git a/src/Entities/EditedMessage.php b/src/Entities/EditedMessage.php
deleted file mode 100644
index 3f652b5c8..000000000
--- a/src/Entities/EditedMessage.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
-
-class EditedMessage extends Message
-{
-
-}
diff --git a/src/Entities/EncryptedCredentials.php b/src/Entities/EncryptedCredentials.php
new file mode 100644
index 000000000..1d57fcf6b
--- /dev/null
+++ b/src/Entities/EncryptedCredentials.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string getData()   Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication
+ * @method string getHash()   Base64-encoded data hash for data authentication
+ * @method string getSecret() Base64-encoded secret, encrypted with the bot's public RSA key, required for data decryption
+ */
+class EncryptedCredentials extends Entity
+{
+    //
+}
diff --git a/src/Entities/EncryptedPassportElement.php b/src/Entities/EncryptedPassportElement.php
new file mode 100644
index 000000000..64f039aa6
--- /dev/null
+++ b/src/Entities/EncryptedPassportElement.php
@@ -0,0 +1,55 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string              getType()        Element type. One of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”, “phone_number”, “email”.
+ * @method string|null         getData()        Optional. Base64-encoded encrypted Telegram Passport element data provided by the user; available only for “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport” and “address” types. Can be decrypted and verified using the accompanying EncryptedCredentials.
+ * @method string|null         getPhoneNumber() Optional. User's verified phone number; available only for “phone_number” type
+ * @method string|null         getEmail()       Optional. User's verified email address; available only for “email” type
+ * @method PassportFile[]|null getFiles()       Optional. Array of encrypted files with documents provided by the user; available only for “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials.
+ * @method PassportFile|null   getFrontSide()   Optional. Encrypted file with the front side of the document, provided by the user; available only for “passport”, “driver_license”, “identity_card” and “internal_passport”. The file can be decrypted and verified using the accompanying EncryptedCredentials.
+ * @method PassportFile|null   getReverseSide() Optional. Encrypted file with the reverse side of the document, provided by the user; available only for “driver_license” and “identity_card”. The file can be decrypted and verified using the accompanying EncryptedCredentials.
+ * @method PassportFile|null   getSelfie()      Optional. Encrypted file with the selfie of the user holding a document, provided by the user; available if requested for “passport”, “driver_license”, “identity_card” and “internal_passport”. The file can be decrypted and verified using the accompanying EncryptedCredentials.
+ * @method PassportFile[]|null getTranslation() Optional. Array of encrypted files with translated versions of documents provided by the user; available if requested for “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials.
+ * @method string              getHash()        Base64-encoded element hash for using in PassportElementErrorUnspecified
+ */
+class EncryptedPassportElement extends Entity
+{
+    public const TYPE_PERSONAL_DETAILS = 'personal_details';
+
+    public const TYPE_PASSPORT = 'passport';
+
+    public const TYPE_DRIVER_LICENSE = 'driver_license';
+
+    public const TYPE_IDENTITY_CARD = 'identity_card';
+
+    public const TYPE_INTERNAL_PASSPORT = 'internal_passport';
+
+    public const TYPE_ADDRESS = 'address';
+
+    public const TYPE_UTILITY_BILL = 'utility_bill';
+
+    public const TYPE_BANK_STATEMENT = 'bank_statement';
+
+    public const TYPE_RENTAL_AGREEMENT = 'rental_agreement';
+
+    public const TYPE_PASSPORT_REGISTRATION = 'passport_registration';
+
+    public const TYPE_TEMPORARY_REGISTRATION = 'temporary_registration';
+
+    public const TYPE_PHONE_NUMBER = 'phone_number';
+
+    public const TYPE_EMAIL = 'email';
+
+    protected static function subEntities(): array
+    {
+        return [
+            'files'        => [PassportFile::class],
+            'front_side'   => PassportFile::class,
+            'reverse_side' => PassportFile::class,
+            'selfie'       => PassportFile::class,
+            'translation'  => [PassportFile::class],
+        ];
+    }
+}
diff --git a/src/Entities/Entity.php b/src/Entities/Entity.php
index 378d71518..7c20b7108 100644
--- a/src/Entities/Entity.php
+++ b/src/Entities/Entity.php
@@ -1,313 +1,115 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use BadMethodCallException;
+use JsonSerializable;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
-use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
-use Longman\TelegramBot\Entities\InputMedia\InputMedia;
-
-/**
- * Class Entity
- *
- * This is the base class for all entities.
- *
- * @link https://core.telegram.org/bots/api#available-types
- *
- * @method array  getRawData()     Get the raw data passed to this entity
- * @method string getBotUsername() Return the bot name passed to this entity
- */
-abstract class Entity implements \JsonSerializable
+abstract class Entity implements JsonSerializable
 {
-    public static $fixThumbnailRename = true;
-
-    public $bot_username = '';
-    public $raw_data = [];
-
-    private $fields = [];
-
-    /**
-     * Entity constructor.
-     *
-     * @todo Get rid of the $bot_username, it shouldn't be here!
-     *
-     * @param array  $data
-     * @param string $bot_username
-     */
-    public function __construct(array $data, string $bot_username = '')
-    {
-        $this->bot_username = $bot_username;
-        $this->raw_data     = $data;
-
-        $this->assignMemberVariables($data);
-        $this->validate();
-    }
-
-    /**
-     * Dynamically set a field.
-     *
-     * @param string $name
-     * @param mixed  $value
-     * @return void
-     */
-    public function __set(string $name, $value): void
-    {
-        $this->fields[$name] = $value;
-    }
-
-    /**
-     * Gets a dynamic field.
-     *
-     * @param string $name
-     * @return mixed|null
-     */
-    public function __get(string $name)
-    {
-        return $this->fields[$name] ?? null;
-    }
+    protected array $fields = [];
 
-    /**
-     * Return the data that should be serialized for Telegram.
-     *
-     * @return array
-     */
-    public function jsonSerialize(): array
-    {
-        return $this->fields;
-    }
-
-    /**
-     * Perform to json
-     *
-     * @return string
-     */
-    public function toJson(): string
-    {
-        return json_encode($this);
-    }
-
-    /**
-     * Perform to string
-     *
-     * @return string
-     */
-    public function __toString()
-    {
-        return $this->toJson();
-    }
-
-    /**
-     * Helper to set member variables
-     *
-     * @param array $data
-     */
-    protected function assignMemberVariables(array $data): void
-    {
+    public function __construct(
+        array $data = []
+    ) {
         foreach ($data as $key => $value) {
-            $key = $this->fixThumbnailRename($key);
-            $this->$key = $value;
+            $this->fields[$key] = $value;
         }
-    }
 
-    /**
-     * Get the list of the properties that are themselves Entities
-     *
-     * @return array
-     */
-    protected function subEntities(): array
-    {
-        return [];
+        foreach (static::presetData() as $key => $value) {
+            $this->fields[$key] = $value;
+        }
     }
 
-    /**
-     * Perform any special entity validation
-     */
-    protected function validate(): void
+    public function __get(string $name)
     {
+        return $this->fields[$name] ?? null;
     }
 
-    /**
-     * Get a property from the current Entity
-     *
-     * @param string $property
-     * @param mixed  $default
-     *
-     * @return mixed
-     */
-    public function getProperty(string $property, $default = null)
+    public function __set(string $name, $value): void
     {
-        return $this->$property ?? $default;
+        $this->fields[$name] = $value;
     }
 
-    /**
-     * Return the variable for the called getter or magically set properties dynamically.
-     *
-     * @param $method
-     * @param $args
-     *
-     * @return mixed|null
-     */
-    public function __call($method, $args)
+    public function __call(string $name, array $arguments)
     {
-        $method = $this->fixThumbnailRename($method);
-
-        //Convert method to snake_case (which is the name of the property)
-        $property_name = mb_strtolower(ltrim(preg_replace('/[A-Z]/', '_$0', substr($method, 3)), '_'));
-        $property_name = $this->fixThumbnailRename($property_name);
+        $snakeName = strtolower(ltrim(preg_replace('/[[:upper:]]/', '_$0', $name), '_'));
 
-        $action = substr($method, 0, 3);
-        if ($action === 'get') {
-            $property = $this->getProperty($property_name);
+        if (str_starts_with($snakeName, 'get_')) {
+            // Getter for fields
+            return $this->getField(substr($snakeName, 4));
+        } elseif (str_starts_with($snakeName, 'set_')) {
+            // Setter for fields
+            $this->setField(substr($snakeName, 4), $arguments[0] ?? null);
 
-            if ($property !== null) {
-                //Get all sub-Entities of the current Entity
-                $sub_entities = $this->subEntities();
+            return $this;
+        } elseif (is_subclass_of($this, AllowsBypassingGet::class)) {
+            $fields = $this::fieldsBypassingGet();
 
-                if (isset($sub_entities[$property_name])) {
-                    $class = $sub_entities[$property_name];
+            if (array_key_exists($snakeName, $fields)) {
 
-                    if (is_array($class)) {
-                        return $this->makePrettyObjectArray(reset($class), $property_name);
-                    }
+                return $this->getField($snakeName) ?? $fields[$snakeName];
 
-                    return Factory::resolveEntityClass($class, $property, $this->getProperty('bot_username'));
-                }
+            } elseif (in_array($snakeName, $fields)) {
 
-                return $property;
-            }
-        } elseif ($action === 'set') {
-            // Limit setters to specific classes.
-            if ($this instanceof InlineEntity || $this instanceof InputMedia || $this instanceof Keyboard || $this instanceof KeyboardButton) {
-                $this->$property_name = $args[0];
-                $this->raw_data[$property_name] = $args[0];
+                return $this->getField($snakeName);
 
-                return $this;
             }
         }
 
-        return null;
+        $method = get_class($this) . '::' . $name . '()';
+        throw new BadMethodCallException("Call to undefined method $method");
     }
 
-    /**
-     * BC for renamed thumb -> thumbnail methods and fields
-     *
-     * @todo Remove after a few versions.
-     *
-     * @param string $name
-     * @return string
-     */
-    protected function fixThumbnailRename(string $name): string
+    private function getField(string $name): mixed
     {
-        return self::$fixThumbnailRename ? preg_replace('/([Tt])humb(nail)?/', '$1humbnail', $name, -1, $count) : $name;
+        $data = $this->fields[$name] ?? null;
 
-        /*if ($count) {
-            // Notify user that there are still outdated method calls?
-        }*/
-    }
+        $subEntities = static::subEntities();
+        if (is_null($data) || ! isset($subEntities[$name])) {
+            return $data;
+        }
 
-    /**
-     * Return an array of nice objects from an array of object arrays
-     *
-     * This method is used to generate pretty object arrays
-     * mainly for PhotoSize and Entities object arrays.
-     *
-     * @param string $class
-     * @param string $property_name
-     *
-     * @return array
-     */
-    protected function makePrettyObjectArray(string $class, string $property_name): array
-    {
-        $objects      = [];
-        $bot_username = $this->getProperty('bot_username');
+        if (is_subclass_of($subEntities[$name], Entity::class)) {
+            $class = $subEntities[$name];
 
-        $properties = array_filter($this->getProperty($property_name) ?: []);
-        foreach ($properties as $property) {
-            $objects[] = Factory::resolveEntityClass($class, $property, $bot_username);
+            return new $class($data);
+        } elseif (is_array($subEntities[$name]) && is_subclass_of($subEntities[$name][0], Entity::class)) {
+            // Arrays like PhotoSize[]
+            $class = $subEntities[$name][0];
+
+            return array_map(fn ($item) => new $class($item), $data);
+        } elseif (is_array($subEntities[$name]) && is_array($subEntities[$name][0]) && is_subclass_of($subEntities[$name][0][0], Entity::class)) {
+            // Edge case currently only needed for Keyboards: Array of Array of InlineKeyboardButton
+            $class = $subEntities[$name][0][0];
+
+            return array_map(fn ($row) => array_map(fn ($button) => new $class($button), $row), $data);
         }
 
-        return $objects;
+        return $data;
     }
 
     /**
-     * Escape markdown (v1) special characters
-     *
-     * @see https://core.telegram.org/bots/api#markdown-style
-     *
-     * @param string $string
-     *
-     * @return string
+     * @return array<string, class-string>
      */
-    public static function escapeMarkdown(string $string): string
+    protected static function subEntities(): array
     {
-        return str_replace(
-            ['[', '`', '*', '_',],
-            ['\[', '\`', '\*', '\_',],
-            $string
-        );
+        return [];
     }
 
-    /**
-     * Escape markdown (v2) special characters
-     *
-     * @see https://core.telegram.org/bots/api#markdownv2-style
-     *
-     * @param string $string
-     *
-     * @return string
-     */
-    public static function escapeMarkdownV2(string $string): string
+    protected static function presetData(): array
     {
-        return str_replace(
-            ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'],
-            ['\_', '\*', '\[', '\]', '\(', '\)', '\~', '\`', '\>', '\#', '\+', '\-', '\=', '\|', '\{', '\}', '\.', '\!'],
-            $string
-        );
+        return [];
     }
 
-    /**
-     * Try to mention the user
-     *
-     * Mention the user with the username otherwise print first and last name
-     * if the $escape_markdown argument is true special characters are escaped from the output
-     *
-     * @todo What about MarkdownV2?
-     *
-     * @param bool $escape_markdown
-     *
-     * @return string
-     */
-    public function tryMention($escape_markdown = false): string
+    private function setField(string $name, mixed $value): void
     {
-        // TryMention only makes sense for the User and Chat entity.
-        if (!($this instanceof User || $this instanceof Chat)) {
-            return '';
-        }
-
-        //Try with the username first...
-        $name        = $this->getProperty('username');
-        $is_username = $name !== null;
-
-        if ($name === null) {
-            //...otherwise try with the names.
-            $name      = $this->getProperty('first_name');
-            $last_name = $this->getProperty('last_name');
-            if ($last_name !== null) {
-                $name .= ' ' . $last_name;
-            }
-        }
-
-        if ($escape_markdown) {
-            $name = self::escapeMarkdown($name);
-        }
+        $this->fields[$name] = $value;
+    }
 
-        return ($is_username ? '@' : '') . $name;
+    public function jsonSerialize(): mixed
+    {
+        return array_filter($this->fields, fn ($value) => ! is_null($value));
     }
 }
diff --git a/src/Entities/ExternalReplyInfo.php b/src/Entities/ExternalReplyInfo.php
index e879973da..dda94b4ae 100644
--- a/src/Entities/ExternalReplyInfo.php
+++ b/src/Entities/ExternalReplyInfo.php
@@ -1,51 +1,39 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\MessageOrigin\MessageOrigin;
 
 /**
- * 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
+ * @method MessageOrigin           getOrigin()             Origin of the message replied to by the given message
+ * @method Chat|null               getChat()               Optional. Chat the original message belongs to. Available only if the chat is a supergroup or a channel.
+ * @method int|null                getMessageId()          Optional. Unique message identifier inside the original chat. Available only if the original chat is a supergroup or a channel.
+ * @method LinkPreviewOptions|null getLinkPreviewOptions() Optional. Options used for link preview generation for the original message, if it is a text message
+ * @method Animation|null          getAnimation()          Optional. Message is an animation, information about the animation
+ * @method Audio|null              getAudio()              Optional. Message is an audio file, information about the file
+ * @method Document|null           getDocument()           Optional. Message is a general file, information about the file
+ * @method PaidMediaInfo|null      getPaidMedia()          Optional. Message contains paid media; information about the paid media
+ * @method PhotoSize[]|null        getPhoto()              Optional. Message is a photo, available sizes of the photo
+ * @method Sticker|null            getSticker()            Optional. Message is a sticker, information about the sticker
+ * @method Story|null              getStory()              Optional. Message is a forwarded story
+ * @method Video|null              getVideo()              Optional. Message is a video, information about the video
+ * @method VideoNote|null          getVideoNote()          Optional. Message is a video note, information about the video message
+ * @method Voice|null              getVoice()              Optional. Message is a voice message, information about the file
+ * @method bool                    hasMediaSpoiler()       Optional. True, if the message media is covered by a spoiler animation
+ * @method Contact|null            getContact()            Optional. Message is a shared contact, information about the contact
+ * @method Dice|null               getDice()               Optional. Message is a dice with random value
+ * @method Game|null               getGame()               Optional. Message is a game, information about the game. More about games »
+ * @method Giveaway|null           getGiveaway()           Optional. Message is a scheduled giveaway, information about the giveaway
+ * @method GiveawayWinners|null    getGiveawayWinners()    Optional. A giveaway with public winners was completed
+ * @method Invoice|null            getInvoice()            Optional. Message is an invoice for a payment, information about the invoice. More about payments »
+ * @method Location|null           getLocation()           Optional. Message is a shared location, information about the location
+ * @method Poll|null               getPoll()               Optional. Message is a native poll, information about the poll
+ * @method Venue|null              getVenue()              Optional. Message is a venue, information about the venue
  */
-class ExternalReplyInfo extends Entity
+class ExternalReplyInfo extends Entity implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'origin'               => MessageOrigin::class,
@@ -54,6 +42,7 @@ protected function subEntities(): array
             'animation'            => Animation::class,
             'audio'                => Audio::class,
             'document'             => Document::class,
+            'paid_media'           => PaidMediaInfo::class,
             'photo'                => [PhotoSize::class],
             'sticker'              => Sticker::class,
             'story'                => Story::class,
@@ -71,4 +60,11 @@ protected function subEntities(): array
             'venue'                => Venue::class,
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'has_media_spoiler' => false,
+        ];
+    }
 }
diff --git a/src/Entities/Factory.php b/src/Entities/Factory.php
deleted file mode 100644
index 0741ef999..000000000
--- a/src/Entities/Factory.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities;
-
-abstract class Factory
-{
-    abstract public static function make(array $data, string $bot_username): Entity;
-
-    public static function resolveEntityClass(string $class, $property, string $bot_username = ''): Entity
-    {
-        if (is_a($property, $class)) {
-            return $property;
-        }
-
-        if (is_subclass_of($class, Factory::class)) {
-            return $class::make($property, $bot_username);
-        }
-
-        return new $class($property, $bot_username);
-    }
-}
diff --git a/src/Entities/File.php b/src/Entities/File.php
index a988dad49..69182c8db 100644
--- a/src/Entities/File.php
+++ b/src/Entities/File.php
@@ -1,27 +1,14 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class File
- *
- * @link https://core.telegram.org/bots/api#file
- *
- * @method string getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int    getFileSize()     Optional. File size, if known
- * @method string getFilePath()     Optional. File path. Use https://api.telegram.org/file/bot<token>/<file_path> to get the file.
+ * @method string      getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string      getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int|null    getFileSize()     Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.
+ * @method string|null getFilePath()     Optional. File path. Use https://api.telegram.org/file/bot<token>/<file_path> to get the file.
  */
 class File extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/ForceReply.php b/src/Entities/ForceReply.php
new file mode 100644
index 000000000..87592faa2
--- /dev/null
+++ b/src/Entities/ForceReply.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method true        getForceReply()            Shows reply interface to the user, as if they manually selected the bot's message and tapped 'Reply'
+ * @method string|null getInputFieldPlaceholder() Optional. The placeholder to be shown in the input field when the reply is active; 1-64 characters
+ * @method bool|null   getSelective()             Optional. Use this parameter if you want to force reply from specific users only. Targets: 1), users that are @mentioned in the text of the Message object; 2), if the bot's message is a reply to a message in the same chat and forum topic, sender of the original message.
+ */
+class ForceReply extends Entity
+{
+    //
+}
diff --git a/src/Entities/ForumTopic.php b/src/Entities/ForumTopic.php
new file mode 100644
index 000000000..8714b60c4
--- /dev/null
+++ b/src/Entities/ForumTopic.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int         getMessageThreadId()   Unique identifier of the forum topic
+ * @method string      getName()              Name of the topic
+ * @method int         getIconColor()         Color of the topic icon in RGB format
+ * @method string|null getIconCustomEmojiId() Optional. Unique identifier of the custom emoji shown as the topic icon
+ */
+class ForumTopic extends Entity
+{
+    //
+}
diff --git a/src/Entities/ForumTopicClosed.php b/src/Entities/ForumTopicClosed.php
new file mode 100644
index 000000000..170deb9ba
--- /dev/null
+++ b/src/Entities/ForumTopicClosed.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class ForumTopicClosed extends Entity
+{
+    //
+}
diff --git a/src/Entities/ForumTopicCreated.php b/src/Entities/ForumTopicCreated.php
new file mode 100644
index 000000000..952695481
--- /dev/null
+++ b/src/Entities/ForumTopicCreated.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string      getName()              Name of the topic
+ * @method int         getIconColor()         Color of the topic icon in RGB format
+ * @method string|null getIconCustomEmojiId() Optional. Unique identifier of the custom emoji shown as the topic icon
+ */
+class ForumTopicCreated extends Entity
+{
+    //
+}
diff --git a/src/Entities/ForumTopicEdited.php b/src/Entities/ForumTopicEdited.php
new file mode 100644
index 000000000..47232e037
--- /dev/null
+++ b/src/Entities/ForumTopicEdited.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string|null getName()              Optional. New name of the topic, if it was edited
+ * @method string|null getIconCustomEmojiId() Optional. New identifier of the custom emoji shown as the topic icon, if it was edited; an empty string if the icon was removed
+ */
+class ForumTopicEdited extends Entity
+{
+    //
+}
diff --git a/src/Entities/ForumTopicReopened.php b/src/Entities/ForumTopicReopened.php
new file mode 100644
index 000000000..bdb799452
--- /dev/null
+++ b/src/Entities/ForumTopicReopened.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class ForumTopicReopened extends Entity
+{
+    //
+}
diff --git a/src/Entities/Game.php b/src/Entities/Game.php
new file mode 100644
index 000000000..fddf37c5e
--- /dev/null
+++ b/src/Entities/Game.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string               getTitle()        Title of the game
+ * @method string               getDescription()  Description of the game
+ * @method PhotoSize[]          getPhoto()        Photo that will be displayed in the game message in chats.
+ * @method string|null          getText()         Optional. Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters.
+ * @method MessageEntity[]|null getTextEntities() Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc.
+ * @method Animation|null       getAnimation()    Optional. Animation that will be displayed in the game message in chats. Upload via BotFather
+ */
+class Game extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'photo'         => [PhotoSize::class],
+            'text_entities' => [MessageEntity::class],
+            'animation'     => Animation::class,
+        ];
+    }
+}
diff --git a/src/Entities/Games/CallbackGame.php b/src/Entities/Games/CallbackGame.php
deleted file mode 100644
index 078d80746..000000000
--- a/src/Entities/Games/CallbackGame.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Games;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class CallbackGame
- *
- * A placeholder, currently holds no information. Use BotFather to set up your game.
- *
- * @link https://core.telegram.org/bots/api#callbackgame
- **/
-class CallbackGame extends Entity
-{
-
-}
diff --git a/src/Entities/Games/Game.php b/src/Entities/Games/Game.php
deleted file mode 100644
index cb114941c..000000000
--- a/src/Entities/Games/Game.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Games;
-
-use Longman\TelegramBot\Entities\Animation;
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\MessageEntity;
-use Longman\TelegramBot\Entities\PhotoSize;
-
-/**
- * Class Game
- *
- * This object represents a game. Use BotFather to create and edit games, their short names will act as unique identifiers.
- *
- * @link https://core.telegram.org/bots/api#game
- *
- * @method string          getTitle()        Title of the game
- * @method string          getDescription()  Description of the game
- * @method PhotoSize[]     getPhoto()        Photo that will be displayed in the game message in chats.
- * @method string          getText()         Optional. Brief description of the game or high scores included in the game message. Can be automatically edited to include current high scores for the game when the bot calls setGameScore, or manually edited using editMessageText. 0-4096 characters.
- * @method MessageEntity[] getTextEntities() Optional. Special entities that appear in text, such as usernames, URLs, bot commands, etc.
- * @method Animation       getAnimation()    Optional. Animation that will be displayed in the game message in chats. Upload via BotFather
- **/
-class Game extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'photo'         => [PhotoSize::class],
-            'text_entities' => [MessageEntity::class],
-            'animation'     => Animation::class,
-        ];
-    }
-}
diff --git a/src/Entities/Games/GameHighScore.php b/src/Entities/Games/GameHighScore.php
deleted file mode 100644
index 71760010a..000000000
--- a/src/Entities/Games/GameHighScore.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Games;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
-
-/**
- * Class GameHighScore
- *
- * This object represents one row of the high scores table for a game.
- *
- * @link https://core.telegram.org/bots/api#gamehighscore
- *
- * @method int  getPosition() Position in high score table for the game
- * @method User getUser()     User
- * @method int  getScore()    Score
- **/
-class GameHighScore extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'user' => User::class,
-        ];
-    }
-}
diff --git a/src/Entities/GeneralForumTopicHidden.php b/src/Entities/GeneralForumTopicHidden.php
new file mode 100644
index 000000000..078a89c19
--- /dev/null
+++ b/src/Entities/GeneralForumTopicHidden.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class GeneralForumTopicHidden extends Entity
+{
+    //
+}
diff --git a/src/Entities/GeneralForumTopicUnhidden.php b/src/Entities/GeneralForumTopicUnhidden.php
new file mode 100644
index 000000000..5e8e0974d
--- /dev/null
+++ b/src/Entities/GeneralForumTopicUnhidden.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class GeneralForumTopicUnhidden extends Entity
+{
+    //
+}
diff --git a/src/Entities/Giveaway.php b/src/Entities/Giveaway.php
new file mode 100644
index 000000000..938238f32
--- /dev/null
+++ b/src/Entities/Giveaway.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+
+/**
+ * @method Chat[]        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 true|null     getOnlyNewMembers()                                      Optional. True, if only users who join the chats after the giveaway started should be eligible to win
+ * @method bool          hasPublicWinners()                                       Optional. True, if the list of giveaway winners will be visible to everyone
+ * @method string|null   getPrizeDescription()                                    Optional. Description of additional giveaway prize
+ * @method string[]|null 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|null      getPremiumSubscriptionMonthCount()                       Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for
+ */
+class Giveaway extends Entity implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'chats' => [Chat::class],
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'has_public_winners' => false,
+        ];
+    }
+}
diff --git a/src/Entities/Giveaway/Giveaway.php b/src/Entities/Giveaway/Giveaway.php
deleted file mode 100644
index bd7fe9311..000000000
--- a/src/Entities/Giveaway/Giveaway.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Giveaway;
-
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * This object represents a message about a scheduled giveaway.
- *
- * @link https://core.telegram.org/bots/api#giveaway
- *
- * @method array<Chat>   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<string> 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
deleted file mode 100644
index 294500723..000000000
--- a/src/Entities/Giveaway/GiveawayCompleted.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Giveaway;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\Message;
-
-/**
- * This object represents a service message about the completion of a giveaway without public winners.
- *
- * @link https://core.telegram.org/bots/api#giveawaycompleted
- *
- * @method int     getWinnerCount()         Number of winners in the giveaway
- * @method int     getUnclaimedPrizeCount() Optional. Number of undistributed prizes
- * @method Message getGiveawayMessage()     Optional. Message with the giveaway that was completed, if it wasn't deleted
- */
-class GiveawayCompleted extends Entity
-{
-    protected function subEntities(): array
-    {
-        return [
-            'giveaway_message' => Message::class,
-        ];
-    }
-}
diff --git a/src/Entities/Giveaway/GiveawayCreated.php b/src/Entities/Giveaway/GiveawayCreated.php
deleted file mode 100644
index c4ec8dee3..000000000
--- a/src/Entities/Giveaway/GiveawayCreated.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Giveaway;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class GiveawayCreated extends Entity
-{
-
-}
diff --git a/src/Entities/Giveaway/GiveawayWinners.php b/src/Entities/Giveaway/GiveawayWinners.php
deleted file mode 100644
index 9874fbe10..000000000
--- a/src/Entities/Giveaway/GiveawayWinners.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Giveaway;
-
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
-
-/**
- * This object represents a message about the completion of a giveaway with public winners.
- *
- * @link https://core.telegram.org/bots/api#giveawaywinners
- *
- * @method Chat        getChat()                          The chat that created the giveaway
- * @method int         getGiveawayMessageId()             Identifier of the message with the giveaway in the chat
- * @method int         getWinnersSelectionDate()          Point in time (Unix timestamp) when winners of the giveaway were selected
- * @method int         getWinnerCount()                   Total number of winners in the giveaway
- * @method array<User> 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/GiveawayCompleted.php b/src/Entities/GiveawayCompleted.php
new file mode 100644
index 000000000..1c6b32cea
--- /dev/null
+++ b/src/Entities/GiveawayCompleted.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int          getWinnerCount()         Number of winners in the giveaway
+ * @method int|null     getUnclaimedPrizeCount() Optional. Number of undistributed prizes
+ * @method Message|null getGiveawayMessage()     Optional. Message with the giveaway that was completed, if it wasn't deleted
+ */
+class GiveawayCompleted extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'giveaway_message' => Message::class,
+        ];
+    }
+}
diff --git a/src/Entities/GiveawayCreated.php b/src/Entities/GiveawayCreated.php
new file mode 100644
index 000000000..a93c41f81
--- /dev/null
+++ b/src/Entities/GiveawayCreated.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class GiveawayCreated extends Entity
+{
+    //
+}
diff --git a/src/Entities/GiveawayWinners.php b/src/Entities/GiveawayWinners.php
new file mode 100644
index 000000000..4d00b6e96
--- /dev/null
+++ b/src/Entities/GiveawayWinners.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+
+/**
+ * @method Chat        getChat()                                                The chat that created the giveaway
+ * @method int         getGiveawayMessageId()                                   Identifier of the message with the giveaway in the chat
+ * @method int         getWinnersSelectionDate() Point in time (Unix timestamp) when winners of the giveaway were selected
+ * @method int         getWinnerCount()                                         Total number of winners in the giveaway
+ * @method User[]      getWinners()                                             List of up to 100 winners of the giveaway
+ * @method int|null    getAdditionalChatCount()                                 Optional. The number of other chats the user had to join in order to be eligible for the giveaway
+ * @method int|null    getPremiumSubscriptionMonthCount()                       Optional. The number of months the Telegram Premium subscription won from the giveaway will be active for
+ * @method int|null    getUnclaimedPrizeCount()                                 Optional. Number of undistributed prizes
+ * @method true|null   getOnlyNewMembers()                                      Optional. True, if only users who had joined the chats after the giveaway started were eligible to win
+ * @method bool        wasRefunded()                                            Optional. True, if the giveaway was canceled because the payment for it was refunded
+ * @method string|null getPrizeDescription()                                    Optional. Description of additional giveaway prize
+ */
+class GiveawayWinners extends Entity implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'chat'    => Chat::class,
+            'winners' => [User::class],
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'was_refunded' => false,
+        ];
+    }
+}
diff --git a/src/Entities/InaccessibleMessage.php b/src/Entities/InaccessibleMessage.php
new file mode 100644
index 000000000..9a114ed81
--- /dev/null
+++ b/src/Entities/InaccessibleMessage.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class InaccessibleMessage extends MaybeInaccessibleMessage
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'chat' => Chat::class,
+        ];
+    }
+}
diff --git a/src/Entities/InlineKeyboard.php b/src/Entities/InlineKeyboard.php
deleted file mode 100644
index 30feea28a..000000000
--- a/src/Entities/InlineKeyboard.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
-
-/**
- * Class InlineKeyboard
- *
- * @link https://core.telegram.org/bots/api#inlinekeyboardmarkup
- */
-class InlineKeyboard extends Keyboard
-{
-}
diff --git a/src/Entities/InlineKeyboardButton.php b/src/Entities/InlineKeyboardButton.php
index e23439e5c..c57400027 100644
--- a/src/Entities/InlineKeyboardButton.php
+++ b/src/Entities/InlineKeyboardButton.php
@@ -1,80 +1,27 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * 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\Games\CallbackGame;
-use Longman\TelegramBot\Exception\TelegramException;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class InlineKeyboardButton
- *
- * @link https://core.telegram.org/bots/api#inlinekeyboardbutton
- *
- * @method string       getText()                         Label text on the button
- * @method string       getUrl()                          Optional. HTTP url to be opened when button is pressed
- * @method LoginUrl     getLoginUrl()                     Optional. An HTTP URL used to automatically authorize the user. Can be used as a replacement for the Telegram Login Widget.
- * @method string       getCallbackData()                 Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes
- * @method WebAppInfo   getWebApp()                       Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only in private chats between a user and the bot.
- * @method string       getSwitchInlineQuery()            Optional. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. Can be empty, in which case just the bot’s username will be inserted.
- * @method string       getSwitchInlineQueryCurrentChat() Optional. If set, pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field. Can be empty, in which case only the bot’s username will be inserted.
- * @method SwitchInlineQueryChosenChat getSwitchInlineQueryChosenChat() Optional. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field
- * @method CallbackGame getCallbackGame()                 Optional. Description of the game that will be launched when the user presses the button.
- * @method bool         getPay()                          Optional. Specify True, to send a Pay button.
- *
- * @method $this setText(string $text)                                                     Label text on the button
- * @method $this setUrl(string $url)                                                       Optional. HTTP url to be opened when button is pressed
- * @method $this setLoginUrl(LoginUrl $login_url)                                          Optional. HTTP url to be opened when button is pressed
- * @method $this setCallbackData(string $callback_data)                                    Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes
- * @method $this setWebApp(WebAppInfo $web_app)                                            Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only in private chats between a user and the bot.
- * @method $this setSwitchInlineQuery(string $switch_inline_query)                         Optional. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. Can be empty, in which case just the bot’s username will be inserted.
- * @method $this setSwitchInlineQueryCurrentChat(string $switch_inline_query_current_chat) Optional. If set, pressing the button will insert the bot‘s username and the specified inline query in the current chat's input field. Can be empty, in which case only the bot’s username will be inserted.
- * @method $this setSwitchInlineQueryChosenChat(SwitchInlineQueryChosenChat $switch_inline_query_chosen_chat) Optional. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field
- * @method $this setCallbackGame(CallbackGame $callback_game)                              Optional. Description of the game that will be launched when the user presses the button.
- * @method $this setPay(bool $pay)                                                         Optional. Specify True, to send a Pay button.
+ * @method string                           getText()                        Label text on the button
+ * @method string|null                      getUrl()                         Optional. HTTP or tg:// URL to be opened when the button is pressed. Links tg://user?id=<user_id> can be used to mention a user by their identifier without using a username, if this is allowed by their privacy settings.
+ * @method string|null                      getCallbackData()                Optional. Data to be sent in a callback query to the bot when button is pressed, 1-64 bytes. Not supported for messages sent on behalf of a Telegram Business account.
+ * @method WebAppInfo|null                  getWebApp()                      Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Available only in private chats between a user and the bot. Not supported for messages sent on behalf of a Telegram Business account.
+ * @method LoginUrl|null                    getLoginUrl()                    Optional. An HTTPS URL used to automatically authorize the user. Can be used as a replacement for the Telegram Login Widget.
+ * @method string|null                      getSwitchInlineQuery()           Optional. If set, pressing the button will prompt the user to select one of their chats, open that chat and insert the bot's username and the specified inline query in the input field. May be empty, in which case just the bot's username will be inserted. Not supported for messages sent on behalf of a Telegram Business account.
+ * @method SwitchInlineQueryChosenChat|null getSwitchInlineQueryChosenChat() Optional. If set, pressing the button will prompt the user to select one of their chats of the specified type, open that chat and insert the bot's username and the specified inline query in the input field. Not supported for messages sent on behalf of a Telegram Business account.
+ * @method CallbackGame|null                getCallbackGame()                Optional. Description of the game that will be launched when the user presses the button.
+ * @method bool|null                        getPay()                         Optional. Specify True, to send a Pay button. Substrings “⭐” and “XTR” in the buttons's text will be replaced with a Telegram Star icon.
  */
-class InlineKeyboardButton extends KeyboardButton
+class InlineKeyboardButton extends Entity
 {
-    /**
-     * Check if the passed data array could be an InlineKeyboardButton.
-     *
-     * @param array $data
-     *
-     * @return bool
-     */
-    public static function couldBe(array $data): bool
+    protected static function subEntities(): array
     {
-        return array_key_exists('text', $data) && (
-                array_key_exists('url', $data) ||
-                array_key_exists('login_url', $data) ||
-                array_key_exists('callback_data', $data) ||
-                array_key_exists('web_app', $data) ||
-                array_key_exists('switch_inline_query', $data) ||
-                array_key_exists('switch_inline_query_current_chat', $data) ||
-                array_key_exists('switch_inline_query_chosen_chat', $data) ||
-                array_key_exists('callback_game', $data) ||
-                array_key_exists('pay', $data)
-            );
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    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, ['setUrl', 'setLoginUrl', 'setCallbackData', 'setWebApp', 'setSwitchInlineQuery', 'setSwitchInlineQueryCurrentChat', 'setSwitchInlineQueryChosenChat', 'setCallbackGame', 'setPay'], true)) {
-            unset($this->url, $this->login_url, $this->callback_data, $this->web_app, $this->switch_inline_query, $this->switch_inline_query_current_chat, $this->switch_inline_query_chosen_chat, $this->callback_game, $this->pay);
-        }
-
-        return parent::__call($method, $args);
+        return [
+            'web_app'                         => WebAppInfo::class,
+            'login_url'                       => LoginUrl::class,
+            'switch_inline_query_chosen_chat' => SwitchInlineQueryChosenChat::class,
+            'callback_game'                   => CallbackGame::class,
+        ];
     }
 }
diff --git a/src/Entities/InlineKeyboardMarkup.php b/src/Entities/InlineKeyboardMarkup.php
new file mode 100644
index 000000000..a33fc9d03
--- /dev/null
+++ b/src/Entities/InlineKeyboardMarkup.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method array<array> getInlineKeyboard() Array of button rows, each represented by an Array of InlineKeyboardButton objects
+ */
+class InlineKeyboardMarkup extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'inline_keyboard' => [[InlineKeyboardButton::class]],
+        ];
+    }
+}
diff --git a/src/Entities/InlineQuery.php b/src/Entities/InlineQuery.php
index b6e397cfe..179654c55 100644
--- a/src/Entities/InlineQuery.php
+++ b/src/Entities/InlineQuery.php
@@ -1,57 +1,22 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * 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\InlineQuery\InlineQueryResult;
-use Longman\TelegramBot\Request;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class InlineQuery
- *
- * @link https://core.telegram.org/bots/api#inlinequery
- *
- * @method string   getId()       Unique identifier for this query
- * @method User     getFrom()     Sender
- * @method Location getLocation() Optional. Sender location, only for bots that request user location
- * @method string   getQuery()    Text of the query (up to 512 characters)
- * @method string   getOffset()   Offset of the results to be returned, can be controlled by the bot
- * @method string   getChatType() Optional. Type of the chat, from which the inline query was sent. Can be either “sender” for a private chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always known for requests sent from official clients and most third-party clients, unless the request was sent from a secret chat
+ * @method string        getId()       Unique identifier for this query
+ * @method User          getFrom()     Sender
+ * @method string        getQuery()    Text of the query (up to 256 characters).
+ * @method string        getOffset()   Offset of the results to be returned, can be controlled by the bot
+ * @method string|null   getChatType() Optional. Type of the chat from which the inline query was sent. Can be either “sender” for a private chat with the inline query sender, “private”, “group”, “supergroup”, or “channel”. The chat type should be always known for requests sent from official clients and most third-party clients, unless the request was sent from a secret chat
+ * @method Location|null getLocation() Optional. Sender location, only for bots that request user location
  */
 class InlineQuery extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'from'     => User::class,
             'location' => Location::class,
         ];
     }
-
-    /**
-     * Answer this inline query with the passed results.
-     *
-     * @param InlineQueryResult[] $results
-     * @param array               $data
-     *
-     * @return ServerResponse
-     */
-    public function answer(array $results, array $data = []): ServerResponse
-    {
-        return Request::answerInlineQuery(array_merge([
-            'inline_query_id' => $this->getId(),
-            'results'         => $results,
-        ], $data));
-    }
 }
diff --git a/src/Entities/InlineQuery/InlineEntity.php b/src/Entities/InlineQuery/InlineEntity.php
deleted file mode 100644
index 80e6eeb06..000000000
--- a/src/Entities/InlineQuery/InlineEntity.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class InlineEntity
- *
- * This is the base class for all inline entities.
- */
-abstract class InlineEntity extends Entity
-{
-
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResult.php b/src/Entities/InlineQuery/InlineQueryResult.php
deleted file mode 100644
index f8e038ea8..000000000
--- a/src/Entities/InlineQuery/InlineQueryResult.php
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-interface InlineQueryResult
-{
-
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultArticle.php b/src/Entities/InlineQuery/InlineQueryResultArticle.php
deleted file mode 100644
index 70ef6604e..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultArticle.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultArticle
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultarticle
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'title'                 => '',
- *   'input_message_content' => <InputMessageContent>,
- *   'reply_markup'          => <InlineKeyboard>,
- *   'url'                   => '',
- *   'hide_url'              => true,
- *   'description'           => '',
- *   'thumbnail_url'         => '',
- *   'thumbnail_width'       => 30,
- *   'thumbnail_height'      => 30,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be article
- * @method string               getId()                  Unique identifier for this result, 1-64 Bytes
- * @method string               getTitle()               Title of the result
- * @method InputMessageContent  getInputMessageContent() Content of the message to be sent
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method string               getUrl()                 Optional. URL of the result
- * @method bool                 getHideUrl()             Optional. Pass True, if you don't want the URL to be shown in the message
- * @method string               getDescription()         Optional. Short description of the result
- * @method string               getThumbnailUrl()        Optional. Url of the thumbnail for the result
- * @method int                  getThumbnailWidth()      Optional. Thumbnail width
- * @method int                  getThumbnailHeight()     Optional. Thumbnail height
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 Bytes
- * @method $this setTitle(string $title)                                            Title of the result
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Content of the message to be sent
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setUrl(string $url)                                                Optional. URL of the result
- * @method $this setHideUrl(bool $hide_url)                                         Optional. Pass True, if you don't want the URL to be shown in the message
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setThumbnailUrl(string $thumbnail_url)                             Optional. Url of the thumbnail for the result
- * @method $this setThumbnailWidth(int $thumbnail_width)                            Optional. Thumbnail width
- * @method $this setThumbnailHeight(int $thumbnail_height)                          Optional. Thumbnail height
- */
-class InlineQueryResultArticle extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultArticle constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'article';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultAudio.php b/src/Entities/InlineQuery/InlineQueryResultAudio.php
deleted file mode 100644
index c0a38e2b8..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultAudio.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultAudio
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultaudio
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'audio_url'             => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'performer'             => '',
- *   'audio_duration'        => 123,
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be audio
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getAudioUrl()            A valid URL for the audio file
- * @method string               getTitle()               Title
- * @method string               getCaption()             Optional. Caption, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the audio caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method string               getPerformer()           Optional. Performer
- * @method int                  getAudioDuration()       Optional. Audio duration in seconds
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the audio
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setAudioUrl(string $audio_url)                                     A valid URL for the audio file
- * @method $this setTitle(string $title)                                            Title
- * @method $this setCaption(string $caption)                                        Optional. Caption, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the audio caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setPerformer(string $performer)                                    Optional. Performer
- * @method $this setAudioDuration(int $audio_duration)                              Optional. Audio duration in seconds
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the audio
- */
-class InlineQueryResultAudio extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultAudio constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'audio';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedAudio.php b/src/Entities/InlineQuery/InlineQueryResultCachedAudio.php
deleted file mode 100644
index 0f5525712..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedAudio.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedAudio
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedaudio
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'audio_file_id'         => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be audio
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getAudioFileId()         A valid file identifier for the audio file
- * @method string               getCaption()             Optional. Caption, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the audio caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the audio
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setAudioFileId(string $audio_file_id)                              A valid file identifier for the audio file
- * @method $this setCaption(string $caption)                                        Optional. Caption, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the audio caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the audio
- */
-class InlineQueryResultCachedAudio extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedAudio constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'audio';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedDocument.php b/src/Entities/InlineQuery/InlineQueryResultCachedDocument.php
deleted file mode 100644
index e16b9bf64..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedDocument.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedDocument
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcacheddocument
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'title'                 => '',
- *   'document_file_id'      => '',
- *   'description'           => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be document
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getTitle()               Title for the result
- * @method string               getDocumentFileId()      A valid file identifier for the file
- * @method string               getDescription()         Optional. Short description of the result
- * @method string               getCaption()             Optional. Caption of the document to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the document caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the file
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setTitle(string $title)                                            Title for the result
- * @method $this setDocumentFileId(string $document_file_id)                        A valid file identifier for the file
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the document to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the document caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the file
- */
-class InlineQueryResultCachedDocument extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedDocument constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'document';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedGif.php b/src/Entities/InlineQuery/InlineQueryResultCachedGif.php
deleted file mode 100644
index f1b114b3f..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedGif.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedGif
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedgif
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'gif_file_id'           => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be gif
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getGifFileId()           A valid file identifier for the GIF file
- * @method string               getTitle()               Optional. Title for the result
- * @method string               getCaption()             Optional. Caption of the GIF file to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the GIF animation
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setGifFileId(string $gif_file_id)                                  A valid file identifier for the GIF file
- * @method $this setTitle(string $title)                                            Optional. Title for the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the GIF file to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the GIF animation
- */
-class InlineQueryResultCachedGif extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedGif constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'gif';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedMpeg4Gif.php b/src/Entities/InlineQuery/InlineQueryResultCachedMpeg4Gif.php
deleted file mode 100644
index cf01fcf6f..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedMpeg4Gif.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedMpeg4Gif
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'mpeg4_file_id'         => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be mpeg4_gif
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getMpeg4FileId()         A valid file identifier for the MP4 file
- * @method string               getTitle()               Optional. Title for the result
- * @method string               getCaption()             Optional. Caption of the MPEG-4 file to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the video animation
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setMpeg4FileId(string $mpeg4_file_id)                              A valid file identifier for the MP4 file
- * @method $this setTitle(string $title)                                            Optional. Title for the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the MPEG-4 file to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the video animation
- */
-class InlineQueryResultCachedMpeg4Gif extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedMpeg4Gif constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'mpeg4_gif';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedPhoto.php b/src/Entities/InlineQuery/InlineQueryResultCachedPhoto.php
deleted file mode 100644
index 0eda7b59b..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedPhoto.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedPhoto
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedphoto
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'photo_file_id'         => '',
- *   'title'                 => '',
- *   'description'           => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be photo
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getPhotoFileId()         A valid file identifier of the photo
- * @method string               getTitle()               Optional. Title for the result
- * @method string               getDescription()         Optional. Short description of the result
- * @method string               getCaption()             Optional. Caption of the photo to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the photo caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the photo
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setPhotoFileId(string $photo_file_id)                              A valid file identifier of the photo
- * @method $this setTitle(string $title)                                            Optional. Title for the result
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the photo to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the photo caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the photo
- */
-class InlineQueryResultCachedPhoto extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedPhoto constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'photo';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedSticker.php b/src/Entities/InlineQuery/InlineQueryResultCachedSticker.php
deleted file mode 100644
index 8d7c577c0..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedSticker.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedSticker
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedsticker
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'sticker_file_id'       => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be sticker
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getStickerFileId()       A valid file identifier of the sticker
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the sticker
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setStickerFileId(string $sticker_file_id)                          A valid file identifier of the sticker
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the sticker
- */
-class InlineQueryResultCachedSticker extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedSticker constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'sticker';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedVideo.php b/src/Entities/InlineQuery/InlineQueryResultCachedVideo.php
deleted file mode 100644
index 13163c1ac..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedVideo.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedVideo
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedvideo
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'video_file_id'         => '',
- *   'title'                 => '',
- *   'description'           => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be video
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getVideoFileId()         A valid file identifier for the video file
- * @method string               getTitle()               Title for the result
- * @method string               getDescription()         Optional. Short description of the result
- * @method string               getCaption()             Optional. Caption of the video to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the video caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the video
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setVideoFileId(string $video_file_id)                              A valid file identifier for the video file
- * @method $this setTitle(string $title)                                            Title for the result
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the video to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the video caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the video
- */
-class InlineQueryResultCachedVideo extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedVideo constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'video';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultCachedVoice.php b/src/Entities/InlineQuery/InlineQueryResultCachedVoice.php
deleted file mode 100644
index 6ab041487..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultCachedVoice.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultCachedVoice
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcachedvoice
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'voice_file_id'         => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be voice
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getVoiceFileId()         A valid file identifier for the voice message
- * @method string               getTitle()               Voice message title
- * @method string               getCaption()             Optional. Caption, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the voice caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. An Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the voice message
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setVoiceFileId(string $voice_file_id)                              A valid file identifier for the voice message
- * @method $this setTitle(string $title)                                            Voice message title
- * @method $this setCaption(string $caption)                                        Optional. Caption, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the voice caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. An Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the voice message
- */
-class InlineQueryResultCachedVoice extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultCachedVoice constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'voice';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultContact.php b/src/Entities/InlineQuery/InlineQueryResultContact.php
deleted file mode 100644
index b4d6cfa85..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultContact.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultContact
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultcontact
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'phone_number'          => '',
- *   'first_name'            => '',
- *   'last_name'             => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- *   'thumbnail_url'         => '',
- *   'thumbnail_width'       => 30,
- *   'thumbnail_height'      => 30,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be contact
- * @method string               getId()                  Unique identifier for this result, 1-64 Bytes
- * @method string               getPhoneNumber()         Contact's phone number
- * @method string               getFirstName()           Contact's first name
- * @method string               getLastName()            Optional. Contact's last name
- * @method string               getVcard()               Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the contact
- * @method string               getThumbnailUrl()        Optional. Url of the thumbnail for the result
- * @method int                  getThumbnailWidth()      Optional. Thumbnail width
- * @method int                  getThumbnailHeight()     Optional. Thumbnail height
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 Bytes
- * @method $this setPhoneNumber(string $phone_number)                               Contact's phone number
- * @method $this setFirstName(string $first_name)                                   Contact's first name
- * @method $this setLastName(string $last_name)                                     Optional. Contact's last name
- * @method $this setVcard(string $vcard)                                            Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the contact
- * @method $this setThumbnailUrl(string $thumbnail_url)                             Optional. Url of the thumbnail for the result
- * @method $this setThumbnailWidth(int $thumbnail_width)                            Optional. Thumbnail width
- * @method $this setThumbnailHeight(int $thumbnail_height)                          Optional. Thumbnail height
- */
-class InlineQueryResultContact extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultContact constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'contact';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultDocument.php b/src/Entities/InlineQuery/InlineQueryResultDocument.php
deleted file mode 100644
index 8db76febe..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultDocument.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultDocument
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultdocument
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'document_url'          => '',
- *   'mime_type'             => '',
- *   'description'           => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- *   'thumbnail_url'         => '',
- *   'thumbnail_width'       => 30,
- *   'thumbnail_height'      => 30,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be document
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getTitle()               Title for the result
- * @method string               getCaption()             Optional. Caption of the document to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the document caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method string               getDocumentUrl()         A valid URL for the file
- * @method string               getMimeType()            Mime type of the content of the file, either “application/pdf” or “application/zip”
- * @method string               getDescription()         Optional. Short description of the result
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the file
- * @method string               getThumbnailUrl()        Optional. URL of the thumbnail (jpeg only) for the file
- * @method int                  getThumbnailWidth()      Optional. Thumbnail width
- * @method int                  getThumbnailHeight()     Optional. Thumbnail height
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setTitle(string $title)                                            Title for the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the document to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the document caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setDocumentUrl(string $document_url)                               A valid URL for the file
- * @method $this setMimeType(string $mime_type)                                     Mime type of the content of the file, either “application/pdf” or “application/zip”
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the file
- * @method $this setThumbnailUrl(string $thumbnail_url)                             Optional. URL of the thumbnail (jpeg only) for the file
- * @method $this setThumbnailWidth(int $thumbnail_width)                            Optional. Thumbnail width
- * @method $this setThumbnailHeight(int $thumbnail_height)                          Optional. Thumbnail height
- */
-class InlineQueryResultDocument extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultDocument constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'document';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultGame.php b/src/Entities/InlineQuery/InlineQueryResultGame.php
deleted file mode 100644
index d40860fe4..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultGame.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-
-/**
- * Class InlineQueryResultGame
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultgame
- *
- * <code>
- * $data = [
- *   'id'              => '',
- *   'game_short_name' => '',
- *   'reply_markup'    => <InlineKeyboard>,
- * ];
- * </code>
- *
- * @method string         getType()          Type of the result, must be game
- * @method string         getId()            Unique identifier for this result, 1-64 bytes
- * @method string         getGameShortName() Short name of the game
- * @method InlineKeyboard getReplyMarkup()   Optional. Inline keyboard attached to the message
- *
- * @method $this setId(string $id)                            Unique identifier for this result, 1-64 bytes
- * @method $this setGameShortName(string $game_short_name)    Short name of the game
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup) Optional. Inline keyboard attached to the message
- */
-class InlineQueryResultGame extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultGame constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'game';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultGif.php b/src/Entities/InlineQuery/InlineQueryResultGif.php
deleted file mode 100644
index 34c9810cc..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultGif.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultGif
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultgif
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'gif_url'               => '',
- *   'gif_width'             => 30,
- *   'gif_height'            => 30,
- *   'thumbnail_url'         => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be gif
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getGifUrl()              A valid URL for the GIF file. File size must not exceed 1MB
- * @method int                  getGifWidth()            Optional. Width of the GIF
- * @method int                  getGifHeight()           Optional. Height of the GIF
- * @method int                  getGifDuration()         Optional. Duration of the GIF
- * @method string               getThumbnailUrl()        URL of the static thumbnail for the result (jpeg or gif)
- * @method string               getThumbnailMimeType()   Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
- * @method string               getTitle()               Optional. Title for the result
- * @method string               getCaption()             Optional. Caption of the GIF file to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the GIF animation
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setGifUrl(string $gif_url)                                         A valid URL for the GIF file. File size must not exceed 1MB
- * @method $this setGifWidth(int $gif_width)                                        Optional. Width of the GIF
- * @method $this setGifHeight(int $gif_height)                                      Optional. Height of the GIF
- * @method $this setGifDuration(int $gif_duration)                                  Optional.  Duration of the GIF
- * @method $this setThumbnailUrl(string $thumbnail_url)                             URL of the static thumbnail for the result (jpeg or gif)
- * @method $this setThumbnailMimeType(string $thumbnail_mime_type)                  Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
- * @method $this setTitle(string $title)                                            Optional. Title for the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the GIF file to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the GIF animation
- */
-class InlineQueryResultGif extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultGif constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'gif';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultLocation.php b/src/Entities/InlineQuery/InlineQueryResultLocation.php
deleted file mode 100644
index 888556d4b..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultLocation.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultLocation
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultlocation
- *
- * <code>
- * $data = [
- *   'id'                     => '',
- *   'latitude'               => 36.0338,
- *   'longitude'              => 71.8601,
- *   'title'                  => '',
- *   'horizontal_accuracy'    => 36.9,
- *   'live_period'            => 900,
- *   'heading'                => 88,
- *   'proximity_alert_radius' => 300,
- *   'reply_markup'           => <InlineKeyboard>,
- *   'input_message_content'  => <InputMessageContent>,
- *   'thumbnail_url'          => '',
- *   'thumbnail_width'        => 30,
- *   'thumbnail_height'       => 30,
- * ];
- * </code>
- *
- * @method string              getType()                 Type of the result, must be location
- * @method string              getId()                   Unique identifier for this result, 1-64 Bytes
- * @method float               getLatitude()             Location latitude in degrees
- * @method float               getLongitude()            Location longitude in degrees
- * @method string              getTitle()                Location title
- * @method float               getHorizontalAccuracy()   Optional. The radius of uncertainty for the location, measured in meters; 0-1500
- * @method int                 getLivePeriod()           Optional. Period in seconds for which the location can be updated, should be between 60 and 86400.
- * @method int                 getHeading()              Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
- * @method int                 getProximityAlertRadius() Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.
- * @method InlineKeyboard      getReplyMarkup()          Optional. Inline keyboard attached to the message
- * @method InputMessageContent getInputMessageContent()  Optional. Content of the message to be sent instead of the location
- * @method string              getThumbnailUrl()         Optional. Url of the thumbnail for the result
- * @method int                 getThumbnailWidth()       Optional. Thumbnail width
- * @method int                 getThumbnailHeight()      Optional. Thumbnail height
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 Bytes
- * @method $this setLatitude(float $latitude)                                       Location latitude in degrees
- * @method $this setLongitude(float $longitude)                                     Location longitude in degrees
- * @method $this setTitle(string $title)                                            Location title
- * @method $this setHorizontalAccuracy(float $horizontal_accuracy)                  Optional. The radius of uncertainty for the location, measured in meters; 0-1500
- * @method $this setLivePeriod(int $live_period)                                    Optional. Period in seconds for which the location can be updated, should be between 60 and 86400.
- * @method $this setHeading(int $heading)                                           Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
- * @method $this setProximityAlertRadius(int $proximity_alert_radius)               Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the location
- * @method $this setThumbnailUrl(string $thumbnail_url)                             Optional. Url of the thumbnail for the result
- * @method $this setThumbnailWidth(int $thumbnail_width)                            Optional. Thumbnail width
- * @method $this setThumbnailHeight(int $thumbnail_height)                          Optional. Thumbnail height
- */
-class InlineQueryResultLocation extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultLocation constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'location';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultMpeg4Gif.php b/src/Entities/InlineQuery/InlineQueryResultMpeg4Gif.php
deleted file mode 100644
index 01015eeca..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultMpeg4Gif.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultMpeg4Gif
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'mpeg4_url'             => '',
- *   'mpeg4_width'           => 30,
- *   'mpeg4_height'          => 30,
- *   'thumbnail_url'         => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be mpeg4_gif
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getMpeg4Url()            A valid URL for the MP4 file. File size must not exceed 1MB
- * @method int                  getMpeg4Width()          Optional. Video width
- * @method int                  getMpeg4Height()         Optional. Video height
- * @method int                  getMpeg4Duration()       Optional. Video duration
- * @method string               getThumbnailUrl()        URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result
- * @method string               getThumbnailMimeType()   Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
- * @method string               getTitle()               Optional. Title for the result
- * @method string               getCaption()             Optional. Caption of the MPEG-4 file to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the video animation
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setMpeg4Url(string $mpeg4_url)                                     A valid URL for the MP4 file. File size must not exceed 1MB
- * @method $this setMpeg4Width(int $mpeg4_width)                                    Optional. Video width
- * @method $this setMpeg4Height(int $mpeg4_height)                                  Optional. Video height
- * @method $this setMpeg4Duration(int $mpeg4_duration)                              Optional. Video duration
- * @method $this setThumbnailUrl(string $thumbnail_url)                             URL of the static (JPEG or GIF) or animated (MPEG4) thumbnail for the result
- * @method $this setThumbnailMimeType(string $thumbnail_mime_type)                  Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
- * @method $this setTitle(string $title)                                            Optional. Title for the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the MPEG-4 file to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the video animation
- */
-class InlineQueryResultMpeg4Gif extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultMpeg4Gif constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'mpeg4_gif';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultPhoto.php b/src/Entities/InlineQuery/InlineQueryResultPhoto.php
deleted file mode 100644
index 2633ad8a8..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultPhoto.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultPhoto
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultphoto
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'photo_url'             => '',
- *   'thumbnail_url'         => '',
- *   'photo_width'           => 30,
- *   'photo_height'          => 30,
- *   'title'                 => '',
- *   'description'           => '',
- *   'caption'               => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be photo
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getPhotoUrl()            A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB
- * @method string               getThumbnailUrl()        URL of the thumbnail for the photo
- * @method int                  getPhotoWidth()          Optional. Width of the photo
- * @method int                  getPhotoHeight()         Optional. Height of the photo
- * @method string               getTitle()               Optional. Title for the result
- * @method string               getDescription()         Optional. Short description of the result
- * @method string               getCaption()             Optional. Caption of the photo to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the photo caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the photo
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setPhotoUrl(string $photo_url)                                     A valid URL of the photo. Photo must be in jpeg format. Photo size must not exceed 5MB
- * @method $this setThumbnailUrl(string $thumbnail_url)                             URL of the thumbnail for the photo
- * @method $this setPhotoWidth(int $photo_width)                                    Optional. Width of the photo
- * @method $this setPhotoHeight(int $photo_height)                                  Optional. Height of the photo
- * @method $this setTitle(string $title)                                            Optional. Title for the result
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the photo to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the photo caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the photo
- */
-class InlineQueryResultPhoto extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultPhoto constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'photo';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultVenue.php b/src/Entities/InlineQuery/InlineQueryResultVenue.php
deleted file mode 100644
index 8ab428b00..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultVenue.php
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultVenue
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultvenue
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'latitude'              => 36.0338,
- *   'longitude'             => 71.8601,
- *   'title'                 => '',
- *   'address'               => '',
- *   'foursquare_id'         => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- *   'thumbnail_url'         => '',
- *   'thumbnail_width'       => 30,
- *   'thumbnail_height'      => 30,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be venue
- * @method string               getId()                  Unique identifier for this result, 1-64 Bytes
- * @method float                getLatitude()            Latitude of the venue location in degrees
- * @method float                getLongitude()           Longitude of the venue location in degrees
- * @method string               getTitle()               Title of the venue
- * @method string               getAddress()             Address of the venue
- * @method string               getFoursquareId()        Optional. Foursquare identifier of the venue if known
- * @method string               getFoursquareType()      Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
- * @method string               getGooglePlaceId()       Optional. Google Places identifier of the venue
- * @method string               getGooglePlaceType()     Optional. Google Places type of the venue
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the venue
- * @method string               getThumbnailUrl()        Optional. Url of the thumbnail for the result
- * @method int                  getThumbnailWidth()      Optional. Thumbnail width
- * @method int                  getThumbnailHeight()     Optional. Thumbnail height
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 Bytes
- * @method $this setLatitude(float $latitude)                                       Latitude of the venue location in degrees
- * @method $this setLongitude(float $longitude)                                     Longitude of the venue location in degrees
- * @method $this setTitle(string $title)                                            Title of the venue
- * @method $this setAddress(string $address)                                        Address of the venue
- * @method $this setFoursquareId(string $foursquare_id)                             Optional. Foursquare identifier of the venue if known
- * @method $this setFoursquareType(string $foursquare_type)                         Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
- * @method $this setGooglePlaceId(string $google_place_id)                          Optional. Google Places identifier of the venue
- * @method $this setGooglePlaceType(string $google_place_type)                      Optional. Google Places type of the venue
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the venue
- * @method $this setThumbnailUrl(string $thumbnail_url)                             Optional. Url of the thumbnail for the result
- * @method $this setThumbnailWidth(int $thumbnail_width)                            Optional. Thumbnail width
- * @method $this setThumbnailHeight(int $thumbnail_height)                          Optional. Thumbnail height
- */
-class InlineQueryResultVenue extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultVenue constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'venue';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultVideo.php b/src/Entities/InlineQuery/InlineQueryResultVideo.php
deleted file mode 100644
index 9337bd7a4..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultVideo.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultVideo
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultvideo
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'video_url'             => '',
- *   'mime_type'             => '',
- *   'thumbnail_url'         => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'video_width'           => 30,
- *   'video_height'          => 30,
- *   'video_duration'        => 123,
- *   'description'           => '',
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be video
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getVideoUrl()            A valid URL for the embedded video player or video file
- * @method string               getMimeType()            Mime type of the content of video url, “text/html” or “video/mp4”
- * @method string               getThumbnailUrl()        URL of the thumbnail (jpeg only) for the video
- * @method string               getTitle()               Title for the result
- * @method string               getCaption()             Optional. Caption of the video to be sent, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the video caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method int                  getVideoWidth()          Optional. Video width
- * @method int                  getVideoHeight()         Optional. Video height
- * @method int                  getVideoDuration()       Optional. Video duration in seconds
- * @method string               getDescription()         Optional. Short description of the result
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the video
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setVideoUrl(string $video_url)                                     A valid URL for the embedded video player or video file
- * @method $this setMimeType(string $mime_type)                                     Mime type of the content of video url, “text/html” or “video/mp4”
- * @method $this setThumbnailUrl(string $thumbnail_url)                             URL of the thumbnail (jpeg only) for the video
- * @method $this setTitle(string $title)                                            Title for the result
- * @method $this setCaption(string $caption)                                        Optional. Caption of the video to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the video caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setVideoWidth(int $video_width)                                    Optional. Video width
- * @method $this setVideoHeight(int $video_height)                                  Optional. Video height
- * @method $this setVideoDuration(int $video_duration)                              Optional. Video duration in seconds
- * @method $this setDescription(string $description)                                Optional. Short description of the result
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the video
- */
-class InlineQueryResultVideo extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultVideo constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'video';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQuery/InlineQueryResultVoice.php b/src/Entities/InlineQuery/InlineQueryResultVoice.php
deleted file mode 100644
index 8fbad6972..000000000
--- a/src/Entities/InlineQuery/InlineQueryResultVoice.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InlineQuery;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InputMessageContent\InputMessageContent;
-
-/**
- * Class InlineQueryResultVoice
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultvoice
- *
- * <code>
- * $data = [
- *   'id'                    => '',
- *   'voice_url'             => '',
- *   'title'                 => '',
- *   'caption'               => '',
- *   'voice_duration'        => 123,
- *   'reply_markup'          => <InlineKeyboard>,
- *   'input_message_content' => <InputMessageContent>,
- * ];
- * </code>
- *
- * @method string               getType()                Type of the result, must be voice
- * @method string               getId()                  Unique identifier for this result, 1-64 bytes
- * @method string               getVoiceUrl()            A valid URL for the voice recording
- * @method string               getTitle()               Recording title
- * @method string               getCaption()             Optional. Caption, 0-200 characters
- * @method string               getParseMode()           Optional. Mode for parsing entities in the voice caption
- * @method MessageEntity[]      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method int                  getVoiceDuration()       Optional. Recording duration in seconds
- * @method InlineKeyboard       getReplyMarkup()         Optional. Inline keyboard attached to the message
- * @method InputMessageContent  getInputMessageContent() Optional. Content of the message to be sent instead of the voice recording
- *
- * @method $this setId(string $id)                                                  Unique identifier for this result, 1-64 bytes
- * @method $this setVoiceUrl(string $voice_url)                                     A valid URL for the voice recording
- * @method $this setTitle(string $title)                                            Recording title
- * @method $this setCaption(string $caption)                                        Optional. Caption, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                   Optional. Mode for parsing entities in the voice caption
- * @method $this setCaptionEntities(array $caption_entities)                        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setVoiceDuration(int $voice_duration)                              Optional. Recording duration in seconds
- * @method $this setReplyMarkup(InlineKeyboard $reply_markup)                       Optional. Inline keyboard attached to the message
- * @method $this setInputMessageContent(InputMessageContent $input_message_content) Optional. Content of the message to be sent instead of the voice recording
- */
-class InlineQueryResultVoice extends InlineEntity implements InlineQueryResult
-{
-    /**
-     * InlineQueryResultVoice constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'voice';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResult.php b/src/Entities/InlineQueryResult/InlineQueryResult.php
new file mode 100644
index 000000000..72f42de4e
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResult.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the result
+ * @method string getId()   Unique identifier for this result, 1-64 Bytes
+ */
+class InlineQueryResult extends Entity
+{
+    //
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultArticle.php b/src/Entities/InlineQueryResult/InlineQueryResultArticle.php
new file mode 100644
index 000000000..bb1b2eb0b
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultArticle.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+
+/**
+ * @method string                    getTitle()               Title of the result
+ * @method InputMessageContent       getInputMessageContent() Content of the message to be sent
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method string|null               getUrl()                 Optional. URL of the result
+ * @method bool|null                 getHideUrl()             Optional. Pass True if you don't want the URL to be shown in the message
+ * @method string|null               getDescription()         Optional. Short description of the result
+ * @method string|null               getThumbnailUrl()        Optional. Url of the thumbnail for the result
+ * @method int|null                  getThumbnailWidth()      Optional. Thumbnail width
+ * @method int|null                  getThumbnailHeight()     Optional. Thumbnail height
+ */
+class InlineQueryResultArticle extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'input_message_content' => InputMessageContent::class,
+            'reply_markup'          => InlineKeyboardMarkup::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'article',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultAudio.php b/src/Entities/InlineQueryResult/InlineQueryResultAudio.php
new file mode 100644
index 000000000..8101ff156
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultAudio.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getAudioUrl()            A valid URL for the audio file
+ * @method string                    getTitle()               Title
+ * @method string|null               getCaption()             Optional. Caption, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()           Optional. Mode for parsing entities in the audio caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method string|null               getPerformer()           Optional. Performer
+ * @method int|null                  getAudioDuration()       Optional. Audio duration in seconds
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the audio
+ */
+class InlineQueryResultAudio extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'audio',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedAudio.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedAudio.php
new file mode 100644
index 000000000..a6ebf9bdb
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedAudio.php
@@ -0,0 +1,34 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getAudioFileId()         A valid file identifier for the audio file
+ * @method string|null               getCaption()             Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()           Optional. Mode for parsing entities in the video caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video).
+ */
+class InlineQueryResultCachedAudio extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'audio',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedDocument.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedDocument.php
new file mode 100644
index 000000000..3976b0a7c
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedDocument.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getTitle()               Title for the result
+ * @method string                    getDocumentFileId()      A valid file identifier for the file
+ * @method string|null               getDescription()         Optional. Short description of the result
+ * @method string|null               getCaption()             Optional. Caption of the document to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()           Optional. Mode for parsing entities in the document caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the file
+ */
+class InlineQueryResultCachedDocument extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'document',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedGif.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedGif.php
new file mode 100644
index 000000000..b96e576d9
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedGif.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getGifFileId()             A valid file identifier for the GIF file
+ * @method string|null               getTitle()                 Optional. Title for the result
+ * @method string|null               getCaption()               Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the GIF animation
+ */
+class InlineQueryResultCachedGif extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'gif',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedMpeg4Gif.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedMpeg4Gif.php
new file mode 100644
index 000000000..4c67194c5
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedMpeg4Gif.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getMpeg4FileId()           A valid file identifier for the MPEG4 file
+ * @method string|null               getTitle()                 Optional. Title for the result
+ * @method string|null               getCaption()               Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the video animation
+ */
+class InlineQueryResultCachedMpeg4Gif extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'mpeg4_gif',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedPhoto.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedPhoto.php
new file mode 100644
index 000000000..057a5ca1d
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedPhoto.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getPhotoFileId()           A valid file identifier of the photo
+ * @method string|null               getTitle()                 Optional. Title for the result
+ * @method string|null               getDescription()           Optional. Short description of the result
+ * @method string|null               getCaption()               Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the photo caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the photo
+ */
+class InlineQueryResultCachedPhoto extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'photo',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedSticker.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedSticker.php
new file mode 100644
index 000000000..8d4e09eff
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedSticker.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+
+/**
+ * @method string                    getStickerFileId()       A valid file identifier of the sticker
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the sticker
+ */
+class InlineQueryResultCachedSticker extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'sticker',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedVideo.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedVideo.php
new file mode 100644
index 000000000..de2794838
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedVideo.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getVideoFileId()           A valid file identifier for the video file
+ * @method string                    getTitle()                 Title for the result
+ * @method string|null               getDescription()           Optional. Short description of the result
+ * @method string|null               getCaption()               Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the video caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video).
+ */
+class InlineQueryResultCachedVideo extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'video',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultCachedVoice.php b/src/Entities/InlineQueryResult/InlineQueryResultCachedVoice.php
new file mode 100644
index 000000000..41910617f
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultCachedVoice.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getVoiceFileId()         A valid file identifier for the voice message
+ * @method string                    getTitle()               Voice message title
+ * @method string|null               getCaption()             Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()           Optional. Mode for parsing entities in the video caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video).
+ */
+class InlineQueryResultCachedVoice extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'voice',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultContact.php b/src/Entities/InlineQueryResult/InlineQueryResultContact.php
new file mode 100644
index 000000000..2d2558214
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultContact.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+
+/**
+ * @method string                    getPhoneNumber()         Contact's phone number
+ * @method string                    getFirstName()           Contact's first name
+ * @method string|null               getLastName()            Optional. Contact's last name
+ * @method string|null               getVcard()               Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the contact
+ * @method string|null               getThumbnailUrl()        Optional. Url of the thumbnail for the result
+ * @method int|null                  getThumbnailWidth()      Optional. Thumbnail width
+ * @method int|null                  getThumbnailHeight()     Optional. Thumbnail height
+ */
+class InlineQueryResultContact extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'contact',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultDocument.php b/src/Entities/InlineQueryResult/InlineQueryResultDocument.php
new file mode 100644
index 000000000..64ffb4e8d
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultDocument.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getTitle()               Title for the result
+ * @method string|null               getCaption()             Optional. Caption, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()           Optional. Mode for parsing entities in the audio caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()     Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method string                    getDocumentUrl()         A valid URL for the file
+ * @method string                    getMimeType()            MIME type of the content of the file, either “application/pdf” or “application/zip”
+ * @method string|null               getDescription()         Optional. Short description of the result
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the audio
+ * @method string|null               getThumbnailUrl()        Optional. URL of the thumbnail (JPEG only), for the file
+ * @method int|null                  getThumbnailWidth()      Optional. Thumbnail width
+ * @method int|null                  getThumbnailHeight()     Optional. Thumbnail height
+ */
+class InlineQueryResultDocument extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'document',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultGame.php b/src/Entities/InlineQueryResult/InlineQueryResultGame.php
new file mode 100644
index 000000000..e2bb1df9c
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultGame.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+
+/**
+ * @method string                    getGameShortName() Short name of the game
+ * @method InlineKeyboardMarkup|null getReplyMarkup()   Optional. Inline keyboard attached to the message
+ */
+class InlineQueryResultGame extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'reply_markup' => InlineKeyboardMarkup::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'game',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultGif.php b/src/Entities/InlineQueryResult/InlineQueryResultGif.php
new file mode 100644
index 000000000..d6030f5ba
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultGif.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getGifUrl()                A valid URL for the GIF file. File size must not exceed 1MB
+ * @method int|null                  getGifWidth()              Optional. Width of the GIF
+ * @method int|null                  getGifHeight()             Optional. Height of the GIF
+ * @method int|null                  getGifDuration()           Optional. Duration of the GIF in seconds
+ * @method string                    getThumbnailUrl()          URL of the static (JPEG or GIF), or animated (MPEG4), thumbnail for the result
+ * @method string|null               getThumbnailMimeType()     Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
+ * @method string|null               getTitle()                 Optional. Title for the result
+ * @method string|null               getCaption()               Optional. Caption of the GIF file to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the GIF animation
+ */
+class InlineQueryResultGif extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'gif',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultLocation.php b/src/Entities/InlineQueryResult/InlineQueryResultLocation.php
new file mode 100644
index 000000000..041f2bf8f
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultLocation.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+
+/**
+ * @method float                     getLatitude()             Location latitude in degrees
+ * @method float                     getLongitude()            Location longitude in degrees
+ * @method string                    getTitle()                Location title
+ * @method float|null                getHorizontalAccuracy()   Optional. The radius of uncertainty for the location, measured in meters; 0-1500
+ * @method int|null                  getLivePeriod()           Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely.
+ * @method int|null                  getHeading()              Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
+ * @method int|null                  getProximityAlertRadius() Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.
+ * @method InlineKeyboardMarkup|null getReplyMarkup()          Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()  Optional. Content of the message to be sent instead of the location
+ * @method string|null               getThumbnailUrl()         Optional. Url of the thumbnail for the result
+ * @method int|null                  getThumbnailWidth()       Optional. Thumbnail width
+ * @method int|null                  getThumbnailHeight()      Optional. Thumbnail height
+ */
+class InlineQueryResultLocation extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'location',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultMpeg4Gif.php b/src/Entities/InlineQueryResult/InlineQueryResultMpeg4Gif.php
new file mode 100644
index 000000000..d164758f3
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultMpeg4Gif.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getMpeg4Url()              A valid URL for the MPEG4 file. File size must not exceed 1MB
+ * @method int|null                  getMpeg4Width()            Optional. Video width
+ * @method int|null                  getMpeg4Height()           Optional. Video height
+ * @method int|null                  getMpeg4Duration()         Optional. Video duration in seconds
+ * @method string                    getThumbnailUrl()          URL of the static (JPEG or GIF), or animated (MPEG4), thumbnail for the result
+ * @method string|null               getThumbnailMimeType()     Optional. MIME type of the thumbnail, must be one of “image/jpeg”, “image/gif”, or “video/mp4”. Defaults to “image/jpeg”
+ * @method string|null               getTitle()                 Optional. Title for the result
+ * @method string|null               getCaption()               Optional. Caption of the MPEG-4 file to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the video animation
+ */
+class InlineQueryResultMpeg4Gif extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'mpeg4_gif',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultPhoto.php b/src/Entities/InlineQueryResult/InlineQueryResultPhoto.php
new file mode 100644
index 000000000..277663c2e
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultPhoto.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getPhotoUrl()              A valid URL of the photo. Photo must be in JPEG format. Photo size must not exceed 5MB
+ * @method string                    getThumbnailUrl()          URL of the thumbnail for the photo
+ * @method int|null                  getPhotoWidth()            Optional. Width of the photo
+ * @method int|null                  getPhotoHeight()           Optional. Height of the photo
+ * @method string|null               getTitle()                 Optional. Title for the result
+ * @method string|null               getDescription()           Optional. Short description of the result
+ * @method string|null               getCaption()               Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the photo caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the photo
+ */
+class InlineQueryResultPhoto extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'photo',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultVenue.php b/src/Entities/InlineQueryResult/InlineQueryResultVenue.php
new file mode 100644
index 000000000..d82f81f15
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultVenue.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+
+/**
+ * @method float                     getLatitude()            Latitude of the venue location in degrees
+ * @method float                     getLongitude()           Longitude of the venue location in degrees
+ * @method string                    getTitle()               Title of the venue
+ * @method string                    getAddress()             Address of the venue
+ * @method string|null               getFoursquareId()        Optional. Foursquare identifier of the venue if known
+ * @method string|null               getFoursquareType()      Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.).
+ * @method string|null               getGooglePlaceId()       Optional. Google Places identifier of the venue
+ * @method string|null               getGooglePlaceType()     Optional. Google Places type of the venue. (See supported types.).
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the venue
+ * @method string|null               getThumbnailUrl()        Optional. Url of the thumbnail for the result
+ * @method int|null                  getThumbnailWidth()      Optional. Thumbnail width
+ * @method int|null                  getThumbnailHeight()     Optional. Thumbnail height
+ */
+class InlineQueryResultVenue extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'venue',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultVideo.php b/src/Entities/InlineQueryResult/InlineQueryResultVideo.php
new file mode 100644
index 000000000..e0b92974b
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultVideo.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getVideoUrl()              A valid URL for the embedded video player or video file
+ * @method string                    getMimeType()              MIME type of the content of the video URL, “text/html” or “video/mp4”
+ * @method string                    getThumbnailUrl()          URL of the thumbnail (JPEG only), for the video
+ * @method string                    getTitle()                 Title for the result
+ * @method string|null               getCaption()               Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()             Optional. Mode for parsing entities in the video caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null                 getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method int|null                  getVideoWidth()            Optional. Video width
+ * @method int|null                  getVideoHeigt()            Optional. Video height
+ * @method int|null                  getVideoDuration()         Optional. Video duration in seconds
+ * @method string|null               getDescription()           Optional. Short description of the result
+ * @method InlineKeyboardMarkup|null getReplyMarkup()           Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent()   Optional. Content of the message to be sent instead of the video. This field is required if InlineQueryResultVideo is used to send an HTML-page as a result (e.g., a YouTube video).
+ */
+class InlineQueryResultVideo extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities'      => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'video',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResult/InlineQueryResultVoice.php b/src/Entities/InlineQueryResult/InlineQueryResultVoice.php
new file mode 100644
index 000000000..4094e73c3
--- /dev/null
+++ b/src/Entities/InlineQueryResult/InlineQueryResultVoice.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InlineQueryResult;
+
+use PhpTelegramBot\Core\Entities\InlineKeyboardMarkup;
+use PhpTelegramBot\Core\Entities\InputMessageContent\InputMessageContent;
+use PhpTelegramBot\Core\Entities\MessageEntity;
+
+/**
+ * @method string                    getVoiceUrl()            A valid URL for the voice recording
+ * @method string                    getTitle()               Recording title
+ * @method string|null               getCaption()             Optional. Caption, 0-1024 characters after entities parsing
+ * @method string|null               getParseMode()           Optional. Mode for parsing entities in the voice message caption. See formatting options for more details.
+ * @method MessageEntity[]|null      getCaptionEntites()      Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method int|null                  getVoiceDuration()       Optional. Recording duration in seconds
+ * @method InlineKeyboardMarkup|null getReplyMarkup()         Optional. Inline keyboard attached to the message
+ * @method InputMessageContent|null  getInputMessageContent() Optional. Content of the message to be sent instead of the voice recording
+ */
+class InlineQueryResultVoice extends InlineQueryResult
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entites'       => [MessageEntity::class],
+            'reply_markup'          => InlineKeyboardMarkup::class,
+            'input_message_content' => InputMessageContent::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => 'voice',
+        ];
+    }
+}
diff --git a/src/Entities/InlineQueryResultsButton.php b/src/Entities/InlineQueryResultsButton.php
index a96222e9c..b85049fc6 100644
--- a/src/Entities/InlineQueryResultsButton.php
+++ b/src/Entities/InlineQueryResultsButton.php
@@ -1,24 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class InlineQueryResultsButton
- *
- * This entity represents a button to be shown above inline query results. You must use exactly one of the optional fields.
- *
- * @link https://core.telegram.org/bots/api#inlinequeryresultsbutton
- *
- * @method string     getText()           Label text on the button
- * @method WebAppInfo getWebApp()         Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method switchInlineQuery inside the Web App.
- * @method string     getStartParameter() Optional. Deep-linking parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.
+ * @method string          getText()           Label text on the button
+ * @method WebAppInfo|null getWebApp()         Optional. Description of the Web App that will be launched when the user presses the button. The Web App will be able to switch back to the inline mode using the method switchInlineQuery inside the Web App.
+ * @method string|null     getStartParameter() Optional. Deep-linking parameter for the /start message sent to the bot when a user presses the button. 1-64 characters, only A-Z, a-z, 0-9, _ and - are allowed.
  */
 class InlineQueryResultsButton extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'web_app' => WebAppInfo::class,
diff --git a/src/Entities/InputFile.php b/src/Entities/InputFile.php
new file mode 100644
index 000000000..aad2a1bd5
--- /dev/null
+++ b/src/Entities/InputFile.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Exceptions\InvalidArgumentException;
+
+class InputFile
+{
+    public static function attachFile(string $field, string $filepath): array
+    {
+        $file_id = uniqid($field . '_');
+
+        if (! is_file($filepath)) {
+            throw new InvalidArgumentException("Cannot attach file to '$field'. $filepath must be a valid filepath.");
+        }
+
+        return [
+            $field               => 'attach://' . $file_id,
+            '__file_' . $file_id => fopen($filepath, 'r'),
+        ];
+    }
+}
diff --git a/src/Entities/InputMedia/InputMedia.php b/src/Entities/InputMedia/InputMedia.php
index b9bd973d1..cc23f8cf8 100644
--- a/src/Entities/InputMedia/InputMedia.php
+++ b/src/Entities/InputMedia/InputMedia.php
@@ -1,8 +1,33 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\InputMedia;
+namespace PhpTelegramBot\Core\Entities\InputMedia;
 
-interface InputMedia
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the result
+ */
+class InputMedia extends Entity implements Factory
 {
+    public const TYPE_PHOTO = 'photo';
+
+    public const TYPE_VIDEO = 'video';
+
+    public const TYPE_ANIMATION = 'animation';
+
+    public const TYPE_AUDIO = 'audio';
+
+    public const TYPE_DOCUMENT = 'document';
 
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_PHOTO     => new InputMediaPhoto($data),
+            self::TYPE_VIDEO     => new InputMediaVideo($data),
+            self::TYPE_ANIMATION => new InputMediaAnimation($data),
+            self::TYPE_AUDIO     => new InputMediaAudio($data),
+            self::TYPE_DOCUMENT  => new InputMediaDocument($data),
+        };
+    }
 }
diff --git a/src/Entities/InputMedia/InputMediaAnimation.php b/src/Entities/InputMedia/InputMediaAnimation.php
index 7671923cb..c475cf944 100644
--- a/src/Entities/InputMedia/InputMediaAnimation.php
+++ b/src/Entities/InputMedia/InputMediaAnimation.php
@@ -1,66 +1,43 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMedia;
+namespace PhpTelegramBot\Core\Entities\InputMedia;
 
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\InputFile;
+use PhpTelegramBot\Core\Entities\MessageEntity;
 
 /**
- * Class InputMediaAnimation
- *
- * @link https://core.telegram.org/bots/api#inputmediaanimation
- *
- * <code>
- * $data = [
- *   'media'      => '123abc',
- *   'thumbnail'  => '456def',
- *   'caption'    => '*Animation* caption',
- *   'parse_mode' => 'markdown',
- *   'width'      => 200,
- *   'height'     => 150,
- *   'duration'   => 11,
- * ];
- * </code>
- *
- * @method string          getType()            Type of the result, must be animation
- * @method string          getMedia()           File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files »
- * @method string          getThumbnail()       Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method string          getCaption()         Optional. Caption of the animation to be sent, 0-200 characters
- * @method string          getParseMode()       Optional. Mode for parsing entities in the animation caption
- * @method MessageEntity[] getCaptionEntities() Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method int             getWidth()           Optional. Animation width
- * @method int             getHeight()          Optional. Animation height
- * @method int             getDuration()        Optional. Animation duration
- * @method bool            getHasSpoiler()      Optional. Pass True if the animation needs to be covered with a spoiler animation
- *
- * @method $this setMedia(string $media)                     File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More info on Sending Files »
- * @method $this setThumbnail(string $thumbnail)             Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method $this setCaption(string $caption)                 Optional. Caption of the animation to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)            Optional. Mode for parsing entities in the animation caption
- * @method $this setCaptionEntities(array $caption_entities) Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setWidth(int $width)                        Optional. Animation width
- * @method $this setHeight(int $height)                      Optional. Animation height
- * @method $this setDuration(int $duration)                  Optional. Animation duration
- * @method $this setHasSpoiler(bool $has_spoiler)            Optional. Pass True if the animation needs to be covered with a spoiler animation
+ * @method string               getMedia()                 File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name.
+ * @method string|InputFile     getThumbnail()             Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>.
+ * @method string|null          getCaption()               Optional. Caption of the animation to be sent, 0-1024 characters after entities parsing
+ * @method string|null          getParseMode()             Optional. Mode for parsing entities in the animation caption. See formatting options for more details.
+ * @method MessageEntity[]|null getCaptionEntites()        Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null            getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method int|null             getWidth()                 Optional. Animation width
+ * @method int|null             getHeight()                Optional. Animation height
+ * @method int|null             getDuration()              Optional. Animation duration in seconds
+ * @method bool                 hasSpoiler()               Optional. Pass True if the animation needs to be covered with a spoiler animation
  */
-class InputMediaAnimation extends Entity implements InputMedia
+class InputMediaAnimation extends InputMedia implements AllowsBypassingGet
 {
-    /**
-     * InputMediaAnimation constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities' => [MessageEntity::class],
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_ANIMATION,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
     {
-        $data['type'] = 'animation';
-        parent::__construct($data);
+        return [
+            'has_spoiler' => false,
+        ];
     }
 }
diff --git a/src/Entities/InputMedia/InputMediaAudio.php b/src/Entities/InputMedia/InputMediaAudio.php
index a992095fe..37e04449f 100644
--- a/src/Entities/InputMedia/InputMediaAudio.php
+++ b/src/Entities/InputMedia/InputMediaAudio.php
@@ -1,64 +1,33 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMedia;
+namespace PhpTelegramBot\Core\Entities\InputMedia;
 
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Entities\InputFile;
+use PhpTelegramBot\Core\Entities\MessageEntity;
 
 /**
- * Class InputMediaAudio
- *
- * @link https://core.telegram.org/bots/api#inputmediaaudio
- *
- * <code>
- * $data = [
- *   'media'      => '123abc',
- *   'thumbnail'  => '456def',
- *   'caption'    => '*Audio* caption',
- *   'parse_mode' => 'markdown',
- *   'duration'   => 42,
- *   'performer'  => 'John Doe',
- *   'title'      => 'The Song',
- * ];
- * </code>
- *
- * @method string          getType()            Type of the result, must be audio
- * @method string          getMedia()           File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method string          getThumbnail()       Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method string          getCaption()         Optional. Caption of the audio to be sent, 0-200 characters
- * @method string          getParseMode()       Optional. Mode for parsing entities in the audio caption
- * @method MessageEntity[] getCaptionEntities() Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method int             getDuration()        Optional. Duration of the audio in seconds
- * @method string          getPerformer()       Optional. Performer of the audio
- * @method string          getTitle()           Optional. Title of the audio
- *
- * @method $this setMedia(string $media)                     File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method $this setThumbnail(string $thumbnail)             Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method $this setCaption(string $caption)                 Optional. Caption of the audio to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)            Optional. Mode for parsing entities in the audio caption
- * @method $this setCaptionEntities(array $caption_entities) Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setDuration(int $duration)                  Optional. Duration of the audio in seconds
- * @method $this setPerformer(string $performer)             Optional. Performer of the audio
- * @method $this setTitle(string $title)                     Optional. Title of the audio
+ * @method string               getMedia()           File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name.
+ * @method string|InputFile     getThumbnail()       Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>.
+ * @method string|null          getCaption()         Optional. Caption of the audio to be sent, 0-1024 characters after entities parsing
+ * @method string|null          getParseMode()       Optional. Mode for parsing entities in the audio caption. See formatting options for more details.
+ * @method MessageEntity[]|null getCaptionEntities() Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method int|null             getDuration()        Optional. Duration of the audio in seconds
+ * @method string|null          getPerformer()       Optional. Performer of the audio
+ * @method string|null          getTitle()           Optional. Title of the audio
  */
-class InputMediaAudio extends Entity implements InputMedia
+class InputMediaAudio extends InputMedia
 {
-    /**
-     * InputMediaAudio constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities' => [MessageEntity::class],
+        ];
+    }
+
+    protected static function presetData(): array
     {
-        $data['type'] = 'audio';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_AUDIO,
+        ];
     }
 }
diff --git a/src/Entities/InputMedia/InputMediaDocument.php b/src/Entities/InputMedia/InputMediaDocument.php
index 138e43e8d..e3dff1ff7 100644
--- a/src/Entities/InputMedia/InputMediaDocument.php
+++ b/src/Entities/InputMedia/InputMediaDocument.php
@@ -1,57 +1,31 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMedia;
+namespace PhpTelegramBot\Core\Entities\InputMedia;
 
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Entities\InputFile;
+use PhpTelegramBot\Core\Entities\MessageEntity;
 
 /**
- * Class InputMediaDocument
- *
- * @link https://core.telegram.org/bots/api#inputmediadocument
- *
- * <code>
- * $data = [
- *   'media'      => '123abc',
- *   'thumbnail'  => '456def',
- *   'caption'    => '*Document* caption',
- *   'parse_mode' => 'markdown',
- * ];
- * </code>
- *
- * @method string          getType()                        Type of the result, must be document
- * @method string          getMedia()                       File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method string          getThumbnail()                   Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method string          getCaption()                     Optional. Caption of the document to be sent, 0-200 characters
- * @method string          getParseMode()                   Optional. Mode for parsing entities in the document caption
- * @method MessageEntity[] getCaptionEntities()             Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method bool            getDisableContentTypeDetection() Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always true, if the document is sent as part of an album.
- *
- * @method $this setMedia(string $media)                                              File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method $this setThumbnail(string $thumbnail)                                      Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method $this setCaption(string $caption)                                          Optional. Caption of the document to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)                                     Optional. Mode for parsing entities in the document caption
- * @method $this setCaptionEntities(array $caption_entities)                          Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setDisableContentTypeDetection(bool $disable_content_type_detection) Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always true, if the document is sent as part of an album.
+ * @method string               getMedia()                       File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name.
+ * @method string|InputFile     getThumbnail()                   Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>.
+ * @method string|null          getCaption()                     Optional. Caption of the document to be sent, 0-1024 characters after entities parsing
+ * @method string|null          getParseMode()                   Optional. Mode for parsing entities in the document caption. See formatting options for more details.
+ * @method MessageEntity[]|null getCaptionEntities()             Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null            getDisableContentTypeDetection() Optional. Disables automatic server-side content type detection for files uploaded using multipart/form-data. Always True, if the document is sent as part of an album.
  */
-class InputMediaDocument extends Entity implements InputMedia
+class InputMediaDocument extends InputMedia
 {
-    /**
-     * InputMediaDocument constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entites' => [MessageEntity::class],
+        ];
+    }
+
+    protected static function presetData(): array
     {
-        $data['type'] = 'document';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_DOCUMENT,
+        ];
     }
 }
diff --git a/src/Entities/InputMedia/InputMediaPhoto.php b/src/Entities/InputMedia/InputMediaPhoto.php
index 4a136fccb..4d13ae097 100644
--- a/src/Entities/InputMedia/InputMediaPhoto.php
+++ b/src/Entities/InputMedia/InputMediaPhoto.php
@@ -1,54 +1,38 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMedia;
+namespace PhpTelegramBot\Core\Entities\InputMedia;
 
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\MessageEntity;
 
 /**
- * Class InputMediaPhoto
- *
- * @link https://core.telegram.org/bots/api#inputmediaphoto
- *
- * <code>
- * $data = [
- *   'media'      => '123abc',
- *   'caption'    => '*Photo* caption',
- *   'parse_mode' => 'markdown',
- * ];
- * </code>
- *
- * @method string          getType()            Type of the result, must be photo
- * @method string          getMedia()           File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method string          getCaption()         Optional. Caption of the photo to be sent, 0-200 characters
- * @method string          getParseMode()       Optional. Mode for parsing entities in the photo caption
- * @method MessageEntity[] getCaptionEntities() Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method bool            getHasSpoiler()      Optional. Pass True if the photo needs to be covered with a spoiler animation
- *
- * @method $this setMedia(string $media)                     File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method $this setCaption(string $caption)                 Optional. Caption of the photo to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)            Optional. Mode for parsing entities in the photo caption
- * @method $this setCaptionEntities(array $caption_entities) Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setHasSpoiler(bool $has_spoiler)            Optional. Pass True if the photo needs to be covered with a spoiler animation
+ * @method string               getMedia()                 File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
+ * @method string|null          getCaption()               Optional. Caption of the photo to be sent, 0-1024 characters after entities parsing
+ * @method string|null          getParseMode()             Optional. Mode for parsing entities in the photo caption. See formatting options for more details.
+ * @method MessageEntity[]|null getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null            getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method bool                 hasSpoiler()               Optional. Pass True if the photo needs to be covered with a spoiler animation
  */
-class InputMediaPhoto extends Entity implements InputMedia
+class InputMediaPhoto extends InputMedia implements AllowsBypassingGet
 {
-    /**
-     * InputMediaPhoto constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities' => [MessageEntity::class],
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_PHOTO,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
     {
-        $data['type'] = 'photo';
-        parent::__construct($data);
+        return [
+            'has_spoiler' => false,
+        ];
     }
 }
diff --git a/src/Entities/InputMedia/InputMediaVideo.php b/src/Entities/InputMedia/InputMediaVideo.php
index 3bc10652d..70bbd1735 100644
--- a/src/Entities/InputMedia/InputMediaVideo.php
+++ b/src/Entities/InputMedia/InputMediaVideo.php
@@ -1,69 +1,44 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMedia;
+namespace PhpTelegramBot\Core\Entities\InputMedia;
 
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\InputFile;
+use PhpTelegramBot\Core\Entities\MessageEntity;
 
 /**
- * Class InputMediaVideo
- *
- * @link https://core.telegram.org/bots/api#inputmediavideo
- *
- * <code>
- * $data = [
- *   'media'              => '123abc',
- *   'thumbnail'          => '456def',
- *   'caption'            => '*Video* caption (streamable)',
- *   'parse_mode'         => 'markdown',
- *   'width'              => 800,
- *   'height'             => 600,
- *   'duration'           => 42,
- *   'supports_streaming' => true,
- * ];
- * </code>
- *
- * @method string          getType()              Type of the result, must be video
- * @method string          getMedia()             File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method string          getThumbnail()         Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method string          getCaption()           Optional. Caption of the video to be sent, 0-200 characters
- * @method string          getParseMode()         Optional. Mode for parsing entities in the video caption
- * @method MessageEntity[] getCaptionEntities()   Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method int             getWidth()             Optional. Video width
- * @method int             getHeight()            Optional. Video height
- * @method int             getDuration()          Optional. Video duration
- * @method bool            getSupportsStreaming() Optional. Pass True, if the uploaded video is suitable for streaming
- * @method bool            getHasSpoiler()        Optional. Pass True if the video needs to be covered with a spoiler animation
- *
- * @method $this setMedia(string $media)                        File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass "attach://<file_attach_name>" to upload a new one using multipart/form-data under <file_attach_name> name.
- * @method $this setThumbnail(string $thumbnail)                Optional. Thumbnail of the file sent. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail‘s width and height should not exceed 90. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can’t be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More info on Sending Files »
- * @method $this setCaption(string $caption)                    Optional. Caption of the video to be sent, 0-200 characters
- * @method $this setParseMode(string $parse_mode)               Optional. Mode for parsing entities in the video caption
- * @method $this setCaptionEntities(array $caption_entities)    Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
- * @method $this setWidth(int $width)                           Optional. Video width
- * @method $this setHeight(int $height)                         Optional. Video height
- * @method $this setDuration(int $duration)                     Optional. Video duration
- * @method $this setSupportsStreaming(bool $supports_streaming) Optional. Pass True, if the uploaded video is suitable for streaming
- * @method $this setHasSpoiler(bool $has_spoiler)               Optional. Pass True if the video needs to be covered with a spoiler animation
+ * @method string               getMedia()                 File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name.
+ * @method string|InputFile     getThumbnail()             Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>.
+ * @method string|null          getCaption()               Optional. Caption of the video to be sent, 0-1024 characters after entities parsing
+ * @method string|null          getParseMode()             Optional. Mode for parsing entities in the video caption. See formatting options for more details.
+ * @method MessageEntity[]|null getCaptionEntities()       Optional. List of special entities that appear in the caption, which can be specified instead of parse_mode
+ * @method bool|null            getShowCaptionAboveMedia() Optional. Pass True, if the caption must be shown above the message media
+ * @method int|null             getWidth()                 Optional. Video width
+ * @method int|null             getHeight()                Optional. Video height
+ * @method int|null             getDuration()              Optional. Video duration in seconds
+ * @method bool|null            getSupportsStreaming()     Optional. Pass True if the uploaded video is suitable for streaming
+ * @method bool                 hasSpoiler()               Optional. Pass True if the video needs to be covered with a spoiler animation
  */
-class InputMediaVideo extends Entity implements InputMedia
+class InputMediaVideo extends InputMedia implements AllowsBypassingGet
 {
-    /**
-     * InputMediaVideo constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
+    protected static function subEntities(): array
+    {
+        return [
+            'caption_entities' => [MessageEntity::class],
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_VIDEO,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
     {
-        $data['type'] = 'video';
-        parent::__construct($data);
+        return [
+            'has_spoiler' => false,
+        ];
     }
 }
diff --git a/src/Entities/InputMessageContent/InputContactMessageContent.php b/src/Entities/InputMessageContent/InputContactMessageContent.php
index b954196ad..d3cb97038 100644
--- a/src/Entities/InputMessageContent/InputContactMessageContent.php
+++ b/src/Entities/InputMessageContent/InputContactMessageContent.php
@@ -1,43 +1,14 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMessageContent;
-
-use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
+namespace PhpTelegramBot\Core\Entities\InputMessageContent;
 
 /**
- * Class InputContactMessageContent
- *
- * @link https://core.telegram.org/bots/api#inputcontactmessagecontent
- *
- * <code>
- * $data = [
- *   'phone_number' => '',
- *   'first_name'   => '',
- *   'last_name'    => '',
- *   'vcard'        => '',
- * ];
- * </code>
- *
- * @method string getPhoneNumber() Contact's phone number
- * @method string getFirstName()   Contact's first name
- * @method string getLastName()    Optional. Contact's last name
- * @method string getVcard()       Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes
- *
- * @method $this setPhoneNumber(string $phone_number) Contact's phone number
- * @method $this setFirstName(string $first_name)     Contact's first name
- * @method $this setLastName(string $last_name)       Optional. Contact's last name
- * @method $this setVcard(string $vcard)              Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes
+ * @method string      getPhoneNumber() Contact's phone number
+ * @method string      getFirstName()   Contact's first name
+ * @method string|null getLastName()    Optional. Contact's last name
+ * @method string|null getVcard()       Optional. Additional data about the contact in the form of a vCard, 0-2048 bytes
  */
-class InputContactMessageContent extends InlineEntity implements InputMessageContent
+class InputContactMessageContent extends InputMessageContent
 {
-
+    //
 }
diff --git a/src/Entities/InputMessageContent/InputInvoiceMessageContent.php b/src/Entities/InputMessageContent/InputInvoiceMessageContent.php
index 443635994..ba746d250 100644
--- a/src/Entities/InputMessageContent/InputInvoiceMessageContent.php
+++ b/src/Entities/InputMessageContent/InputInvoiceMessageContent.php
@@ -1,66 +1,45 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMessageContent;
+namespace PhpTelegramBot\Core\Entities\InputMessageContent;
 
-use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\LabeledPrice;
 
 /**
- * Class InputTextMessageContent
- *
- * @link https://core.telegram.org/bots/api#inputinvoicemessagecontent
- *
  * @method string         getTitle()                     Product name, 1-32 characters
  * @method string         getDescription()               Product description, 1-255 characters
  * @method string         getPayload()                   Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
- * @method string         getProviderToken()             Payment provider token, obtained via Botfather
- * @method string         getCurrency()                  Three-letter ISO 4217 currency code, see more on currencies
- * @method LabeledPrice[] getPrices()                    Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
- * @method int            getMaxTipAmount()              Optional. The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a maximum tip of US$1.45 pass max_tip_amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0
- * @method int[]          getSuggestedTipAmounts()       Optional. A JSON-serialized array of suggested amounts of tip in the smallest units of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount.
- * @method string         getProviderData()              Optional. A JSON-serialized object for data about the invoice, which will be shared with the payment provider. A detailed description of the required fields should be provided by the payment provider.
- * @method string         getPhotoUrl()                  Optional. URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
- * @method int            getPhotoSize()                 Optional. Photo size
- * @method int            getPhotoWidth()                Optional. Photo width
- * @method int            getPhotoHeight()               Optional. Photo height
- * @method bool           getNeedName()                  Optional. Pass True, if you require the user's full name to complete the order
- * @method bool           getNeedPhoneNumber()           Optional. Pass True, if you require the user's phone number to complete the order
- * @method bool           getNeedEmail()                 Optional. Pass True, if you require the user's email address to complete the order
- * @method bool           getNeedShippingAddress()       Optional. Pass True, if you require the user's shipping address to complete the order
- * @method bool           getSendPhoneNumberToProvider() Optional. Pass True, if user's phone number should be sent to provider
- * @method bool           getSendEmailToProvider()       Optional. Pass True, if user's email address should be sent to provider
- * @method bool           getIsFlexible()                Optional. Pass True, if the final price depends on the shipping method
- *
- * @method $this setTitle(string $title)                                           Product name, 1-32 characters
- * @method $this setDescription(string $description)                               Product description, 1-255 characters
- * @method $this setPayload(string $payload)                                       Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user, use for your internal processes.
- * @method $this setProviderToken(string $provider_token)                          Payment provider token, obtained via Botfather
- * @method $this setCurrency(string $currency)                                     Three-letter ISO 4217 currency code, see more on currencies
- * @method $this setPrices(LabeledPrice[] $prices)                                 Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
- * @method $this setMaxTipAmount(int $max_tip_amount)                              Optional. The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a maximum tip of US$1.45 pass max_tip_amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0
- * @method $this setSuggestedTipAmounts(int[] $suggested_tip_amounts)              Optional. A JSON-serialized array of suggested amounts of tip in the smallest units of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount.
- * @method $this setProviderData(string $provider_data)                            Optional. A JSON-serialized object for data about the invoice, which will be shared with the payment provider. A detailed description of the required fields should be provided by the payment provider.
- * @method $this setPhotoUrl(string $photo_url)                                    Optional. URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service. People like it better when they see what they are paying for.
- * @method $this setPhotoSize(int $photo_size)                                     Optional. Photo size
- * @method $this setPhotoWidth(int $photo_width)                                   Optional. Photo width
- * @method $this setPhotoHeight(int $photo_height)                                 Optional. Photo height
- * @method $this setNeedName(bool $need_name)                                      Optional. Pass True, if you require the user's full name to complete the order
- * @method $this setNeedPhoneNumber(bool $need_phone_number)                       Optional. Pass True, if you require the user's phone number to complete the order
- * @method $this setNeedEmail(bool $need_email)                                    Optional. Pass True, if you require the user's email address to complete the order
- * @method $this setNeedShippingAddress(bool $need_shipping_address)               Optional. Pass True, if you require the user's shipping address to complete the order
- * @method $this setSendPhoneNumberToProvider(bool $send_phone_number_to_provider) Optional. Pass True, if user's phone number should be sent to provider
- * @method $this setSendEmailToProvider(bool $send_email_to_provider)              Optional. Pass True, if user's email address should be sent to provider
- * @method $this setIsFlexible(bool $is_flexible)                                  Optional. Pass True, if the final price depends on the shipping method
+ * @method string|null    getProviderToken()             Optional. Payment provider token, obtained via @BotFather. Pass an empty string for payments in Telegram Stars.
+ * @method string         getCurrency()                  Three-letter ISO 4217 currency code, see more on currencies. Pass “XTR” for payments in Telegram Stars.
+ * @method LabeledPrice[] getPrices()                    Price breakdown, a JSON-serialized list of components (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.). Must contain exactly one item for payments in Telegram Stars.
+ * @method int|null       getMaxTipAmount()              Optional. The maximum accepted amount for tips in the smallest units of the currency (integer, not float/double). For example, for a maximum tip of US$ 1.45 pass max_tip_amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies). Defaults to 0. Not supported for payments in Telegram Stars.
+ * @method int[]|null     getSuggestedTipAmounts()       Optional. A JSON-serialized array of suggested amounts of tip in the smallest units of the currency (integer, not float/double). At most 4 suggested tip amounts can be specified. The suggested tip amounts must be positive, passed in a strictly increased order and must not exceed max_tip_amount.
+ * @method string|null    getProviderData()              Optional. A JSON-serialized object for data about the invoice, which will be shared with the payment provider. A detailed description of the required fields should be provided by the payment provider.
+ * @method string|null    getPhotoUrl()                  Optional. URL of the product photo for the invoice. Can be a photo of the goods or a marketing image for a service.
+ * @method int|null       getPhotoSize()                 Optional. Photo size in bytes
+ * @method int|null       getPhotoWidth()                Optional. Photo width
+ * @method int|null       getPhotoHeight()               Optional. Photo height
+ * @method bool|null      getNeedName()                  Optional. Pass True if you require the user's full name to complete the order. Ignored for payments in Telegram Stars.
+ * @method bool|null      getNeedPhoneNumber()           Optional. Pass True if you require the user's phone number to complete the order. Ignored for payments in Telegram Stars.
+ * @method bool|null      getNeedEmail()                 Optional. Pass True if you require the user's email address to complete the order. Ignored for payments in Telegram Stars.
+ * @method bool|null      getNeedShippingAddress()       Optional. Pass True if you require the user's shipping address to complete the order. Ignored for payments in Telegram Stars.
+ * @method bool|null      getSendPhoneNumberToProvider() Optional. Pass True if the user's phone number should be sent to the provider. Ignored for payments in Telegram Stars.
+ * @method bool|null      getSendEmailToProvider()       Optional. Pass True if the user's email address should be sent to the provider. Ignored for payments in Telegram Stars.
+ * @method bool           isFlexible()                   Optional. Pass True if the final price depends on the shipping method. Ignored for payments in Telegram Stars.
  */
-class InputInvoiceMessageContent extends InlineEntity implements InputMessageContent
+class InputInvoiceMessageContent extends InputMessageContent implements AllowsBypassingGet
 {
+    protected static function subEntities(): array
+    {
+        return [
+            'prices' => [LabeledPrice::class],
+        ];
+    }
 
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_flexible' => false,
+        ];
+    }
 }
diff --git a/src/Entities/InputMessageContent/InputLocationMessageContent.php b/src/Entities/InputMessageContent/InputLocationMessageContent.php
index 2ede0d2ae..c6a61109c 100644
--- a/src/Entities/InputMessageContent/InputLocationMessageContent.php
+++ b/src/Entities/InputMessageContent/InputLocationMessageContent.php
@@ -1,48 +1,16 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMessageContent;
-
-use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
+namespace PhpTelegramBot\Core\Entities\InputMessageContent;
 
 /**
- * Class InputLocationMessageContent
- *
- * @link https://core.telegram.org/bots/api#inputlocationmessagecontent
- *
- * <code>
- * $data = [
- *   'latitude'               => 36.0338,
- *   'longitude'              => 71.8601,
- *   'horizontal_accuracy'    => 36.9,
- *   'live_period'            => 900,
- *   'heading'                => 88,
- *   'proximity_alert_radius' => 300,
- * ];
- *
- * @method float getLatitude()             Latitude of the location in degrees
- * @method float getLongitude()            Longitude of the location in degrees
- * @method float getHorizontalAccuracy()   Optional. The radius of uncertainty for the location, measured in meters; 0-1500
- * @method int   getLivePeriod()           Optional. Period in seconds for which the location can be updated, should be between 60 and 86400.
- * @method int   getHeading()              Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
- * @method int   getProximityAlertRadius() Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.
- *
- * @method $this setLatitude(float $latitude)                         Latitude of the location in degrees
- * @method $this setLongitude(float $longitude)                       Longitude of the location in degrees
- * @method $this setHorizontalAccuracy(float $horizontal_accuracy)    Optional. The radius of uncertainty for the location, measured in meters; 0-1500
- * @method $this setLivePeriod(int $live_period)                      Optional. Period in seconds for which the location can be updated, should be between 60 and 86400.
- * @method $this setHeading(int $heading)                             Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
- * @method $this setProximityAlertRadius(int $proximity_alert_radius) Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.
+ * @method float      getLatitude()             Latitude of the location in degrees
+ * @method float      getLongitude()            Longitude of the location in degrees
+ * @method float|null getHorizontalAccuracy()   Optional. The radius of uncertainty for the location, measured in meters; 0-1500
+ * @method int|null   getLivePeriod()           Optional. Period in seconds during which the location can be updated, should be between 60 and 86400, or 0x7FFFFFFF for live locations that can be edited indefinitely.
+ * @method int|null   getHeading()              Optional. For live locations, a direction in which the user is moving, in degrees. Must be between 1 and 360 if specified.
+ * @method int|null   getProximityAlertRadius() Optional. For live locations, a maximum distance for proximity alerts about approaching another chat member, in meters. Must be between 1 and 100000 if specified.
  */
-class InputLocationMessageContent extends InlineEntity implements InputMessageContent
+class InputLocationMessageContent extends InputMessageContent
 {
-
+    //
 }
diff --git a/src/Entities/InputMessageContent/InputMessageContent.php b/src/Entities/InputMessageContent/InputMessageContent.php
index 3b43be9e9..79604a3a7 100644
--- a/src/Entities/InputMessageContent/InputMessageContent.php
+++ b/src/Entities/InputMessageContent/InputMessageContent.php
@@ -1,8 +1,10 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\InputMessageContent;
+namespace PhpTelegramBot\Core\Entities\InputMessageContent;
 
-interface InputMessageContent
-{
+use PhpTelegramBot\Core\Entities\Entity;
 
+class InputMessageContent extends Entity
+{
+    //
 }
diff --git a/src/Entities/InputMessageContent/InputTextMessageContent.php b/src/Entities/InputMessageContent/InputTextMessageContent.php
index 574f93af0..bfe3ddf22 100644
--- a/src/Entities/InputMessageContent/InputTextMessageContent.php
+++ b/src/Entities/InputMessageContent/InputTextMessageContent.php
@@ -1,34 +1,23 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities\InputMessageContent;
 
-namespace Longman\TelegramBot\Entities\InputMessageContent;
-
-use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
+use PhpTelegramBot\Core\Entities\LinkPreviewOptions;
+use PhpTelegramBot\Core\Entities\MessageEntity;
 
 /**
- * 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
- *
- * @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 $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
+ * @method string                  getMessageText()        Text of the message to be sent, 1-4096 characters
+ * @method string|null             getParseMode()          Optional. Mode for parsing entities in the message text. See formatting options for more details.
+ * @method MessageEntity[]|null    getEntities()           Optional. List of special entities that appear in message text, which can be specified instead of parse_mode
+ * @method LinkPreviewOptions|null getLinkPreviewOptions() Optional. Link preview generation options for the message
  */
-class InputTextMessageContent extends InlineEntity implements InputMessageContent
+class InputTextMessageContent extends InputMessageContent
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'entities'             => [MessageEntity::class],
+            'link_preview_options' => LinkPreviewOptions::class,
+        ];
+    }
 }
diff --git a/src/Entities/InputMessageContent/InputVenueMessageContent.php b/src/Entities/InputMessageContent/InputVenueMessageContent.php
index ec5b12a8e..d17fb4f61 100644
--- a/src/Entities/InputMessageContent/InputVenueMessageContent.php
+++ b/src/Entities/InputMessageContent/InputVenueMessageContent.php
@@ -1,53 +1,18 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\InputMessageContent;
-
-use Longman\TelegramBot\Entities\InlineQuery\InlineEntity;
+namespace PhpTelegramBot\Core\Entities\InputMessageContent;
 
 /**
- * Class InputVenueMessageContent
- *
- * @link https://core.telegram.org/bots/api#inputvenuemessagecontent
- *
- * <code>
- * $data = [
- *   'latitude'        => 36.0338,
- *   'longitude'       => 71.8601,
- *   'title'           => '',
- *   'address'         => '',
- *   'foursquare_id'   => '',
- *   'foursquare_type' => '',
- * ];
- * </code>
- *
- * @method float  getLatitude()        Latitude of the location in degrees
- * @method float  getLongitude()       Longitude of the location in degrees
- * @method string getTitle()           Name of the venue
- * @method string getAddress()         Address of the venue
- * @method string getFoursquareId()    Optional. Foursquare identifier of the venue, if known
- * @method string getFoursquareType()  Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
- * @method string getGooglePlaceId()   Optional. Google Places identifier of the venue
- * @method string getGooglePlaceType() Optional. Google Places type of the venue
- *
- * @method $this setLatitude(float $latitude)                  Latitude of the location in degrees
- * @method $this setLongitude(float $longitude)                Longitude of the location in degrees
- * @method $this setTitle(string $title)                       Name of the venue
- * @method $this setAddress(string $address)                   Address of the venue
- * @method $this setFoursquareId(string $foursquare_id)        Optional. Foursquare identifier of the venue, if known
- * @method $this setFoursquareType(string $foursquare_type)    Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
- * @method $this setGooglePlaceId(string $google_place_id)     Optional. Google Places identifier of the venue
- * @method $this setGooglePlaceType(string $google_place_type) Optional. Google Places type of the venue
+ * @method float       getLatitude()        Latitude of the venue in degrees
+ * @method float       getLongitude()       Longitude of the venue in degrees
+ * @method string      getTitle()           Name of the venue
+ * @method string      getAddress()         Address of the venue
+ * @method string|null getFoursquareId()    Optional. Foursquare identifier of the venue, if known
+ * @method string|null getFoursquareType()  Optional. Foursquare type of the venue, if known. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.).
+ * @method string|null getGooglePlaceId()   Optional. Google Places identifier of the venue
+ * @method string|null getGooglePlaceType() Optional. Google Places type of the venue. (See supported types.).
  */
-class InputVenueMessageContent extends InlineEntity implements InputMessageContent
+class InputVenueMessageContent extends InputMessageContent
 {
-
+    //
 }
diff --git a/src/Entities/InputPaidMedia/InputPaidMedia.php b/src/Entities/InputPaidMedia/InputPaidMedia.php
new file mode 100644
index 000000000..17ab2221d
--- /dev/null
+++ b/src/Entities/InputPaidMedia/InputPaidMedia.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InputPaidMedia;
+
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the media
+ */
+abstract class InputPaidMedia extends Entity
+{
+    public const TYPE_PHOTO = 'photo';
+
+    public const TYPE_VIDEO = 'video';
+}
diff --git a/src/Entities/InputPaidMedia/InputPaidMediaPhoto.php b/src/Entities/InputPaidMedia/InputPaidMediaPhoto.php
new file mode 100644
index 000000000..809cef283
--- /dev/null
+++ b/src/Entities/InputPaidMedia/InputPaidMediaPhoto.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InputPaidMedia;
+
+/**
+ * @method string getMedia() File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name.
+ */
+class InputPaidMediaPhoto extends InputPaidMedia
+{
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_PHOTO,
+        ];
+    }
+}
diff --git a/src/Entities/InputPaidMedia/InputPaidMediaVideo.php b/src/Entities/InputPaidMedia/InputPaidMediaVideo.php
new file mode 100644
index 000000000..549bdc4e7
--- /dev/null
+++ b/src/Entities/InputPaidMedia/InputPaidMediaVideo.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\InputPaidMedia;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\InputFile;
+
+/**
+ * @method string           getMedia()          File to send. Pass a file_id to send a file that exists on the Telegram servers (recommended), pass an HTTP URL for Telegram to get a file from the Internet, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. More information on Sending Files »
+ * @method InputFile|string getThumbnail()      Optional. Thumbnail of the file sent; can be ignored if thumbnail generation for the file is supported server-side. The thumbnail should be in JPEG format and less than 200 kB in size. A thumbnail's width and height should not exceed 320. Ignored if the file is not uploaded using multipart/form-data. Thumbnails can't be reused and can be only uploaded as a new file, so you can pass “attach://<file_attach_name>” if the thumbnail was uploaded using multipart/form-data under <file_attach_name>. More information on Sending Files »
+ * @method int|null         getWidth()          Optional. Video width
+ * @method int|null         getHeight()         Optional. Video height
+ * @method int|null         getDuration()       Optional. Video duration in seconds
+ * @method bool|null        supportsStreaming() Optional. Pass True if the uploaded video is suitable for streaming
+ */
+class InputPaidMediaVideo extends InputPaidMedia implements AllowsBypassingGet
+{
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_VIDEO,
+        ];
+    }
+
+    protected static function subEntities(): array
+    {
+        return [
+            'thumbnail' => InputFile::class,
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'supports_streaming' => false,
+        ];
+    }
+}
diff --git a/src/Entities/InputPollOption.php b/src/Entities/InputPollOption.php
new file mode 100644
index 000000000..9b8957a09
--- /dev/null
+++ b/src/Entities/InputPollOption.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string               getText()          Option text, 1-100 characters
+ * @method string|null          getTextParseMode() Optional. Mode for parsing entities in the text. See formatting options for more details. Currently, only custom emoji entities are allowed
+ * @method MessageEntity[]|null getTextEntities()  Optional. A JSON-serialized list of special entities that appear in the poll option text. It can be specified instead of text_parse_mode
+ */
+class InputPollOption extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'text_entities' => [MessageEntity::class],
+        ];
+    }
+}
diff --git a/src/Entities/InputSticker.php b/src/Entities/InputSticker.php
index 2fa40e018..eb40b1470 100644
--- a/src/Entities/InputSticker.php
+++ b/src/Entities/InputSticker.php
@@ -1,20 +1,21 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class InputSticker
- *
- * This entity describes a sticker to be added to a sticker set.
- *
- * @link https://core.telegram.org/bots/api#inputsticker
- *
- * @method string       getSticker()      The added sticker. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, upload a new one using multipart/form-data, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. Animated and video stickers can't be uploaded via HTTP URL. More information on Sending Files »
- * @method string[]     getEmojiList()    List of 1-20 emoji associated with the sticker
- * @method MaskPosition getMaskPosition() Optional. Position where the mask should be placed on faces. For “mask” stickers only.
- * @method string[]     getKeywords()     Optional. List of 0-20 search keywords for the sticker with total length of up to 64 characters. For “regular” and “custom_emoji” stickers only.
+ * @method InputFile|string  getSticker()      The added sticker. Pass a file_id as a String to send a file that already exists on the Telegram servers, pass an HTTP URL as a String for Telegram to get a file from the Internet, upload a new one using multipart/form-data, or pass “attach://<file_attach_name>” to upload a new one using multipart/form-data under <file_attach_name> name. Animated and video stickers can't be uploaded via HTTP URL.
+ * @method string            getFormat()       Format of the added sticker, must be one of “static” for a .WEBP or .PNG image, “animated” for a .TGS animation, “video” for a WEBM video
+ * @method string[]          getEmojiList()    List of 1-20 emoji associated with the sticker
+ * @method MaskPosition|null getMaskPosition() Optional. Position where the mask should be placed on faces. For “mask” stickers only.
+ * @method string[]|null     getKeywords()     Optional. List of 0-20 search keywords for the sticker with total length of up to 64 characters. For “regular” and “custom_emoji” stickers only.
  */
 class InputSticker extends Entity
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'sticker'       => InputFile::class,
+            'mask_position' => MaskPosition::class,
+        ];
+    }
 }
diff --git a/src/Entities/Invoice.php b/src/Entities/Invoice.php
new file mode 100644
index 000000000..ee34fbf24
--- /dev/null
+++ b/src/Entities/Invoice.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string getTitle()          Product name
+ * @method string getDescription()    Product description
+ * @method string getStartParameter() Unique bot deep-linking parameter that can be used to generate this invoice
+ * @method string getCurrency()       Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars
+ * @method int    getTotalAmount()    Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+ */
+class Invoice extends Entity
+{
+    //
+}
diff --git a/src/Entities/Keyboard.php b/src/Entities/Keyboard.php
deleted file mode 100644
index eda4ed71c..000000000
--- a/src/Entities/Keyboard.php
+++ /dev/null
@@ -1,231 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- *
- * Written by Marco Boretto <marco.bore@gmail.com>
- */
-
-namespace Longman\TelegramBot\Entities;
-
-use Longman\TelegramBot\Exception\TelegramException;
-
-/**
- * Class Keyboard
- *
- * @link https://core.telegram.org/bots/api#replykeyboardmarkup
- *
- * @method bool   getIsPersistent()          Optional. Requests clients to always show the keyboard when the regular keyboard is hidden. Defaults to false, in which case the custom keyboard can be hidden and opened with a keyboard icon.
- * @method bool   getResizeKeyboard()        Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
- * @method bool   getOneTimeKeyboard()       Optional. Requests clients to remove the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again. Defaults to false.
- * @method string getInputFieldPlaceholder() Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters
- * @method bool   getSelective()             Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
- *
- * @method $this setIsPersistent(bool $is_persistent)                      Optional. Requests clients to always show the keyboard when the regular keyboard is hidden. Defaults to false, in which case the custom keyboard can be hidden and opened with a keyboard icon.
- * @method $this setResizeKeyboard(bool $resize_keyboard)                  Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
- * @method $this setOneTimeKeyboard(bool $one_time_keyboard)               Optional. Requests clients to remove the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat – the user can press a special button in the input field to see the custom keyboard again. Defaults to false.
- * @method $this setInputFieldPlaceholder(string $input_field_placeholder) Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters
- * @method $this setSelective(bool $selective)                             Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message.
- */
-class Keyboard extends Entity
-{
-    public function __construct()
-    {
-        $data = $this->createFromParams(...func_get_args());
-        parent::__construct($data);
-
-        // Remove any empty buttons.
-        $this->{$this->getKeyboardType()} = array_filter($this->{$this->getKeyboardType()});
-    }
-
-    /**
-     * If this keyboard is an inline keyboard.
-     *
-     * @return bool
-     */
-    public function isInlineKeyboard(): bool
-    {
-        return $this instanceof InlineKeyboard;
-    }
-
-    /**
-     * Get the proper keyboard button class for this keyboard.
-     *
-     * @return string
-     */
-    public function getKeyboardButtonClass(): string
-    {
-        return $this->isInlineKeyboard() ? InlineKeyboardButton::class : KeyboardButton::class;
-    }
-
-    /**
-     * Get the type of keyboard, either "inline_keyboard" or "keyboard".
-     *
-     * @return string
-     */
-    public function getKeyboardType(): string
-    {
-        return $this->isInlineKeyboard() ? 'inline_keyboard' : 'keyboard';
-    }
-
-    /**
-     * If no explicit keyboard is passed, try to create one from the parameters.
-     *
-     * @return array
-     */
-    protected function createFromParams(): array
-    {
-        $keyboard_type = $this->getKeyboardType();
-
-        $args = func_get_args();
-
-        // Force button parameters into individual rows.
-        foreach ($args as &$arg) {
-            !is_array($arg) && $arg = [$arg];
-        }
-        unset($arg);
-
-        $data = reset($args);
-
-        if ($from_data = array_key_exists($keyboard_type, (array) $data)) {
-            $args = $data[$keyboard_type];
-
-            // Make sure we're working with a proper row.
-            if (!is_array($args)) {
-                $args = [];
-            }
-        }
-
-        $new_keyboard = [];
-        foreach ($args as $row) {
-            $new_keyboard[] = $this->parseRow($row);
-        }
-
-        if (!empty($new_keyboard)) {
-            if (!$from_data) {
-                $data = [];
-            }
-            $data[$keyboard_type] = $new_keyboard;
-        }
-
-        // If $args was empty, $data still contains `false`
-        return $data ?: [];
-    }
-
-    /**
-     * Create a new row in keyboard and add buttons.
-     *
-     * @return Keyboard
-     */
-    public function addRow(): Keyboard
-    {
-        if (($new_row = $this->parseRow(func_get_args())) !== null) {
-            // Workaround for "Indirect modification of overloaded property has no effect" notice in PHP 8.2.
-            // https://stackoverflow.com/a/19749730/3757422
-            $keyboard                         = $this->{$this->getKeyboardType()};
-            $keyboard[]                       = $new_row;
-            $this->{$this->getKeyboardType()} = $keyboard;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Parse a given row to the correct array format.
-     *
-     * @param array|string $row
-     *
-     * @return array|null
-     */
-    protected function parseRow($row): ?array
-    {
-        if (!is_array($row)) {
-            return null;
-        }
-
-        $new_row = [];
-        foreach ($row as $button) {
-            if (($new_button = $this->parseButton($button)) !== null) {
-                $new_row[] = $new_button;
-            }
-        }
-
-        return $new_row;
-    }
-
-    /**
-     * Parse a given button to the correct KeyboardButton object type.
-     *
-     * @param array|string|KeyboardButton $button
-     *
-     * @return KeyboardButton|null
-     */
-    protected function parseButton($button): ?KeyboardButton
-    {
-        $button_class = $this->getKeyboardButtonClass();
-
-        if ($button instanceof $button_class) {
-            return $button;
-        }
-
-        if (!$this->isInlineKeyboard() || call_user_func([$button_class, 'couldBe'], $button)) {
-            return new $button_class($button);
-        }
-
-        return null;
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    protected function validate(): void
-    {
-        $keyboard_type = $this->getKeyboardType();
-        $keyboard      = $this->getProperty($keyboard_type);
-
-        if ($keyboard !== null) {
-            if (!is_array($keyboard)) {
-                throw new TelegramException($keyboard_type . ' field is not an array!');
-            }
-
-            foreach ($keyboard as $item) {
-                if (!is_array($item)) {
-                    throw new TelegramException($keyboard_type . ' subfield is not an array!');
-                }
-            }
-        }
-    }
-
-    /**
-     * Remove the current custom keyboard and display the default letter-keyboard.
-     *
-     * @link https://core.telegram.org/bots/api/#replykeyboardremove
-     *
-     * @param array $data
-     *
-     * @return Keyboard
-     */
-    public static function remove(array $data = []): Keyboard
-    {
-        return new static(array_merge(['keyboard' => [], 'remove_keyboard' => true, 'selective' => false], $data));
-    }
-
-    /**
-     * Display a reply interface to the user (act as if the user has selected the bot's message and tapped 'Reply').
-     *
-     * @link https://core.telegram.org/bots/api#forcereply
-     *
-     * @param array $data
-     *
-     * @return Keyboard
-     */
-    public static function forceReply(array $data = []): Keyboard
-    {
-        return new static(array_merge(['keyboard' => [], 'force_reply' => true, 'selective' => false], $data));
-    }
-}
diff --git a/src/Entities/KeyboardButton.php b/src/Entities/KeyboardButton.php
index 25cc7f043..fc9a90813 100644
--- a/src/Entities/KeyboardButton.php
+++ b/src/Entities/KeyboardButton.php
@@ -1,60 +1,19 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class KeyboardButton
- *
- * This object represents one button of the reply keyboard. For simple text buttons String can be used instead of this object to specify text of the button. Optional fields request_contact, request_location, and request_poll are mutually exclusive.
- *
- * @link https://core.telegram.org/bots/api#keyboardbutton
- *
- * @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 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 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.
+ * @method string                          getText()            Text of the button. If none of the optional fields are used, it will be sent as a message when the button is pressed
+ * @method KeyboardButtonRequestUsers|null 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|null  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|null                       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|null                       getRequestLocation() Optional. If True, the user's current location will be sent when the button is pressed. Available in private chats only.
+ * @method KeyboardButtonPollType|null     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|null                 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.
  */
 class KeyboardButton extends Entity
 {
-    /**
-     * @param array|string $data
-     */
-    public function __construct($data)
-    {
-        if (is_string($data)) {
-            $data = ['text' => $data];
-        }
-        parent::__construct($data);
-    }
-
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'request_users' => KeyboardButtonRequestUsers::class,
@@ -63,29 +22,4 @@ protected function subEntities(): array
             'web_app'       => WebAppInfo::class,
         ];
     }
-
-    /**
-     * Check if the passed data array could be a KeyboardButton.
-     *
-     * @param array $data
-     *
-     * @return bool
-     */
-    public static function couldBe(array $data): bool
-    {
-        return array_key_exists('text', $data);
-    }
-
-    /**
-     * {@inheritdoc}
-     */
-    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, ['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/KeyboardButtonPollType.php b/src/Entities/KeyboardButtonPollType.php
index f11023123..361ee418b 100644
--- a/src/Entities/KeyboardButtonPollType.php
+++ b/src/Entities/KeyboardButtonPollType.php
@@ -1,28 +1,11 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class KeyboardButtonPollType
- *
- * This entity represents type of a poll, which is allowed to be created and sent when the corresponding button is pressed.
- *
- * @link https://core.telegram.org/bots/api#keyboardbutton
- *
- * @method string getType() Optional. If 'quiz' is passed, the user will be allowed to create only polls in the quiz mode. If 'regular' is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type.
- *
- * @method $this setType(string $type) Optional. If 'quiz' is passed, the user will be allowed to create only polls in the quiz mode. If 'regular' is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type.
+ * @method string|null getType() Optional. If quiz is passed, the user will be allowed to create only polls in the quiz mode. If regular is passed, only regular polls will be allowed. Otherwise, the user will be allowed to create a poll of any type.
  */
 class KeyboardButtonPollType extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/KeyboardButtonRequestChat.php b/src/Entities/KeyboardButtonRequestChat.php
index 02cdf29fe..aa26d4a3e 100644
--- a/src/Entities/KeyboardButtonRequestChat.php
+++ b/src/Entities/KeyboardButtonRequestChat.php
@@ -1,33 +1,27 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class KeyboardButtonRequestChat
- *
- * This entity defines the criteria used to request a suitable chat. The identifier of the selected chat will be shared with the bot when the corresponding button is pressed.
- *
- * @link https://core.telegram.org/bots/api#keyboardbuttonrequestchat
- *
- * @method int getRequestId()                                   Signed 32-bit identifier of the request, which will be received back in the ChatShared object. Must be unique within the message
- * @method bool getChatIsChannel()                              Pass True to request a channel chat, pass False to request a group or a supergroup chat.
- * @method bool getChatIsForum()                                Optional. Pass True to request a forum supergroup, pass False to request a non-forum chat. If not specified, no additional restrictions are applied.
- * @method bool getChatHasUsername()                            Optional. Pass True to request a supergroup or a channel with a username, pass False to request a chat without a username. If not specified, no additional restrictions are applied.
- * @method bool getChatIsCreated()                              Optional. Pass True to request a chat owned by the user. Otherwise, no additional restrictions are applied.
- * @method ChatAdministratorRights getUserAdministratorRights() Optional. A JSON-serialized object listing the required administrator rights of the user in the chat. The rights must be a superset of bot_administrator_rights. If not specified, no additional restrictions are applied.
- * @method ChatAdministratorRights getBotAdministratorRights()  Optional. A JSON-serialized object listing the required administrator rights of the bot in the chat. The rights must be a subset of user_administrator_rights. If not specified, no additional restrictions are applied.
- * @method bool getBotIsMember()                                Optional. Pass True to request a chat with the bot as a member. Otherwise, no additional restrictions are applied.
- *
- * @method $this setRequestId(int $request_id)                                                  Signed 32-bit identifier of the request, which will be received back in the ChatShared object. Must be unique within the message
- * @method $this setChatIsChannel(bool $chat_is_channel)                                        Pass True to request a channel chat, pass False to request a group or a supergroup chat.
- * @method $this setChatIsForum(bool $chat_is_forum)                                            Optional. Pass True to request a forum supergroup, pass False to request a non-forum chat. If not specified, no additional restrictions are applied.
- * @method $this setChatHasUsername(bool $chat_has_username)                                    Optional. Pass True to request a supergroup or a channel with a username, pass False to request a chat without a username. If not specified, no additional restrictions are applied.
- * @method $this setChatIsCreated(bool $chat_is_created)                                        Optional. Pass True to request a chat owned by the user. Otherwise, no additional restrictions are applied.
- * @method $this setUserAdministratorRights(ChatAdministratorRights $user_administrator_rights) Optional. A JSON-serialized object listing the required administrator rights of the user in the chat. The rights must be a superset of bot_administrator_rights. If not specified, no additional restrictions are applied.
- * @method $this setBotAdministratorRights(ChatAdministratorRights $bot_administrator_rights)   Optional. A JSON-serialized object listing the required administrator rights of the bot in the chat. The rights must be a subset of user_administrator_rights. If not specified, no additional restrictions are applied.
- * @method $this setBotIsMember(bool $bot_is_member)                                            Optional. Pass True to request a chat with the bot as a member. Otherwise, no additional restrictions are applied.
+ * @method int                          getRequestId()               Signed 32-bit identifier of the request, which will be received back in the ChatShared object. Must be unique within the message
+ * @method bool                         getChatIsChannel()           Pass True to request a channel chat, pass False to request a group or a supergroup chat.
+ * @method bool|null                    getChatIsForum()             Optional. Pass True to request a forum supergroup, pass False to request a non-forum chat. If not specified, no additional restrictions are applied.
+ * @method bool|null                    getChatHasUsername()         Optional. Pass True to request a supergroup or a channel with a username, pass False to request a chat without a username. If not specified, no additional restrictions are applied.
+ * @method bool|null                    getChatIsCreated()           Optional. Pass True to request a chat owned by the user. Otherwise, no additional restrictions are applied.
+ * @method ChatAdministratorRights|null getUserAdministratorRights() Optional. A JSON-serialized object listing the required administrator rights of the user in the chat. The rights must be a superset of bot_administrator_rights. If not specified, no additional restrictions are applied.
+ * @method ChatAdministratorRights|null getBotAdministratorRights()  Optional. A JSON-serialized object listing the required administrator rights of the bot in the chat. The rights must be a subset of user_administrator_rights. If not specified, no additional restrictions are applied.
+ * @method bool|null                    getBotIsMember()             Optional. Pass True to request a chat with the bot as a member. Otherwise, no additional restrictions are applied.
+ * @method bool|null                    getRequestTitle()            Optional. Pass True to request the chat's title
+ * @method bool|null                    getRequestUsername()         Optional. Pass True to request the chat's username
+ * @method bool|null                    getRequestPhoto()            Optional. Pass True to request the chat's photo
  */
 class KeyboardButtonRequestChat extends Entity
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'user_administrator_rights' => ChatAdministratorRights::class,
+            'bot_administrator_rights'  => ChatAdministratorRights::class,
+        ];
+    }
 }
diff --git a/src/Entities/KeyboardButtonRequestUsers.php b/src/Entities/KeyboardButtonRequestUsers.php
index 945bafa9b..adbb0902c 100644
--- a/src/Entities/KeyboardButtonRequestUsers.php
+++ b/src/Entities/KeyboardButtonRequestUsers.php
@@ -1,23 +1,17 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * 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.
- *
- * @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.
+ * @method int       getRequestId()       Signed 32-bit identifier of the request that will be received back in the UsersShared object. Must be unique within the message
+ * @method bool|null getUserIsBot()       Optional. Pass True to request bots, pass False to request regular users. If not specified, no additional restrictions are applied.
+ * @method bool|null getUserIsPremium()   Optional. Pass True to request premium users, pass False to request non-premium users. If not specified, no additional restrictions are applied.
+ * @method int|null  getMaxQuantity()     Optional. The maximum number of users to be selected; 1-10. Defaults to 1.
+ * @method bool|null getRequestName()     Optional. Pass True to request the users' first and last names
+ * @method bool|null getRequestUsername() Optional. Pass True to request the users' usernames
+ * @method bool|null getRequestPhoto()    Optional. Pass True to request the users' photos
  */
 class KeyboardButtonRequestUsers extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/LabeledPrice.php b/src/Entities/LabeledPrice.php
new file mode 100644
index 000000000..6369cd027
--- /dev/null
+++ b/src/Entities/LabeledPrice.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string getLabel()  Portion label
+ * @method int    getAmount() Price of the product in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+ */
+class LabeledPrice extends Entity
+{
+    //
+}
diff --git a/src/Entities/LinkPreviewOptions.php b/src/Entities/LinkPreviewOptions.php
index 7838e86b3..ab99ca466 100644
--- a/src/Entities/LinkPreviewOptions.php
+++ b/src/Entities/LinkPreviewOptions.php
@@ -1,28 +1,23 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * 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 *
+ * @method bool        isDisabled()          Optional. True, if the link preview is disabled
+ * @method string|null 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|null   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|null   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|null   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
+class LinkPreviewOptions extends Entity implements AllowsBypassingGet
 {
-
+    //
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_disabled'=> false,
+        ];
+    }
 }
diff --git a/src/Entities/Location.php b/src/Entities/Location.php
index b4c67fe52..57946d02b 100644
--- a/src/Entities/Location.php
+++ b/src/Entities/Location.php
@@ -1,29 +1,16 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Location
- *
- * @link https://core.telegram.org/bots/api#location
- *
- * @method float getLongitude()            Longitude as defined by sender
- * @method float getLatitude()             Latitude as defined by sender
- * @method float getHorizontalAccuracy()   Optional. The radius of uncertainty for the location, measured in meters; 0-1500
- * @method int   getLivePeriod()           Optional. Time relative to the message sending date, during which the location can be updated, in seconds. For active live locations only.
- * @method int   getHeading()              Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only.
- * @method int   getProximityAlertRadius() Optional. Maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only.
+ * @method float      getLatitude()             Latitude as defined by sender
+ * @method float      getLongitude()            Longitude as defined by sender
+ * @method float|null getHorizontalAccuracy()   Optional. The radius of uncertainty for the location, measured in meters; 0-1500
+ * @method int|null   getLivePeriod()           Optional. Time relative to the message sending date, during which the location can be updated; in seconds. For active live locations only.
+ * @method int|null   getHeading()              Optional. The direction in which user is moving, in degrees; 1-360. For active live locations only.
+ * @method int|null   getProximityAlertRadius() Optional. The maximum distance for proximity alerts about approaching another chat member, in meters. For sent live locations only.
  */
 class Location extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/LoginUrl.php b/src/Entities/LoginUrl.php
index 9d4c22a6b..fd34986cf 100644
--- a/src/Entities/LoginUrl.php
+++ b/src/Entities/LoginUrl.php
@@ -1,29 +1,14 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class LoginUrl
- *
- * This object represents a parameter of the inline keyboard button used to automatically authorize a user.
- *
- * @link https://core.telegram.org/bots/api#loginurl
- *
- * @method string getUrl()                An HTTP URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.
- * @method string getForwardText()        Optional. New text of the button in forwarded messages.
- * @method string getBotUsername()        Optional. Username of a bot, which will be used for user authorization. See Setting up a bot for more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details.
- * @method bool   getRequestWriteAccess() Optional. Pass True to request the permission for your bot to send messages to the user.
+ * @method string      getUrl()                An HTTPS URL to be opened with user authorization data added to the query string when the button is pressed. If the user refuses to provide authorization data, the original URL without information about the user will be opened. The data added is the same as described in Receiving authorization data.
+ * @method string|null getForwardText()        Optional. New text of the button in forwarded messages.
+ * @method string|null getBotUsername()        Optional. Username of a bot, which will be used for user authorization. See Setting up a bot for more details. If not specified, the current bot's username will be assumed. The url's domain must be the same as the domain linked with the bot. See Linking your domain to the bot for more details.
+ * @method bool|null   getRequestWriteAccess() Optional. Pass True to request the permission for your bot to send messages to the user.
  */
 class LoginUrl extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/MaskPosition.php b/src/Entities/MaskPosition.php
index 76b62425b..a2d623ccb 100644
--- a/src/Entities/MaskPosition.php
+++ b/src/Entities/MaskPosition.php
@@ -1,21 +1,8 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class MaskPosition
- *
- * @link https://core.telegram.org/bots/api#maskposition
- *
  * @method string getPoint()  The part of the face relative to which the mask should be placed. One of “forehead”, “eyes”, “mouth”, or “chin”.
  * @method float  getXShift() Shift by X-axis measured in widths of the mask scaled to the face size, from left to right. For example, choosing -1.0 will place mask just to the left of the default mask position.
  * @method float  getYShift() Shift by Y-axis measured in heights of the mask scaled to the face size, from top to bottom. For example, 1.0 will place the mask just below the default mask position.
@@ -23,5 +10,11 @@
  */
 class MaskPosition extends Entity
 {
+    public const POINT_FOREHEAD = 'forehead';
+
+    public const POINT_EYES = 'eyes';
+
+    public const POINT_MOUTH = 'mouth';
 
+    public const POINT_CHIN = 'chin';
 }
diff --git a/src/Entities/MaybeInaccessibleMessage.php b/src/Entities/MaybeInaccessibleMessage.php
new file mode 100644
index 000000000..41bde29a6
--- /dev/null
+++ b/src/Entities/MaybeInaccessibleMessage.php
@@ -0,0 +1,28 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+
+/**
+ * @method Chat getChat()      Chat the message belonged to
+ * @method int  getMessageId() Unique message identifier inside the chat
+ * @method int  getDate()      The field can be used to differentiate regular and inaccessible messages.
+ */
+class MaybeInaccessibleMessage extends Entity implements Factory
+{
+    public static function make(array $data): static
+    {
+        return match (true) {
+            $data['date'] === 0 => new InaccessibleMessage($data),
+            default             => new Message($data),
+        };
+    }
+
+    protected static function subEntities(): array
+    {
+        return [
+            'chat' => Chat::class,
+        ];
+    }
+}
diff --git a/src/Entities/MenuButton/Factory.php b/src/Entities/MenuButton/Factory.php
deleted file mode 100644
index 86fbf9fa4..000000000
--- a/src/Entities/MenuButton/Factory.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\MenuButton;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class Factory extends \Longman\TelegramBot\Entities\Factory
-{
-    public static function make(array $data, string $bot_username): Entity
-    {
-        $type = [
-            'commands' => MenuButtonCommands::class,
-            'web_app'  => MenuButtonWebApp::class,
-            'default'  => MenuButtonDefault::class,
-        ];
-
-        if (! isset($type[$data['type'] ?? ''])) {
-            return new MenuButtonNotImplemented($data, $bot_username);
-        }
-
-        $class = $type[$data['type']];
-        return new $class($data, $bot_username);
-    }
-}
diff --git a/src/Entities/MenuButton/MenuButton.php b/src/Entities/MenuButton/MenuButton.php
index 216d947ae..31f30fe3e 100644
--- a/src/Entities/MenuButton/MenuButton.php
+++ b/src/Entities/MenuButton/MenuButton.php
@@ -1,15 +1,27 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MenuButton;
+namespace PhpTelegramBot\Core\Entities\MenuButton;
 
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
 
 /**
  * @method string getType() Type of the button
- *
- * @method $this setType($type) Type of the button
  */
-abstract class MenuButton extends Entity
+class MenuButton extends Entity implements Factory
 {
+    public const TYPE_COMMANDS = 'commands';
 
+    public const TYPE_WEB_APP = 'web_app';
+
+    public const TYPE_DEFAULT = 'default';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_COMMANDS => new MenuButtonCommands($data),
+            self::TYPE_WEB_APP  => new MenuButtonWebApp($data),
+            self::TYPE_DEFAULT  => new MenuButtonDefault($data),
+        };
+    }
 }
diff --git a/src/Entities/MenuButton/MenuButtonCommands.php b/src/Entities/MenuButton/MenuButtonCommands.php
index 87a9168c1..21dee21a1 100644
--- a/src/Entities/MenuButton/MenuButtonCommands.php
+++ b/src/Entities/MenuButton/MenuButtonCommands.php
@@ -1,15 +1,13 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MenuButton;
+namespace PhpTelegramBot\Core\Entities\MenuButton;
 
-/**
- * Represents a menu button, which opens the bot's list of commands.
- */
 class MenuButtonCommands extends MenuButton
 {
-    public function __construct(array $data)
+    protected static function presetData(): array
     {
-        $data['type'] = 'commands';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_COMMANDS,
+        ];
     }
 }
diff --git a/src/Entities/MenuButton/MenuButtonDefault.php b/src/Entities/MenuButton/MenuButtonDefault.php
index 96df21952..7ce86c2e4 100644
--- a/src/Entities/MenuButton/MenuButtonDefault.php
+++ b/src/Entities/MenuButton/MenuButtonDefault.php
@@ -1,15 +1,13 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MenuButton;
+namespace PhpTelegramBot\Core\Entities\MenuButton;
 
-/**
- * Describes that no specific value for the menu button was set.
- */
 class MenuButtonDefault extends MenuButton
 {
-    public function __construct(array $data)
+    protected static function presetData(): array
     {
-        $data['type'] = 'default';
-        parent::__construct($data);
+        return [
+            'type' => self::TYPE_DEFAULT,
+        ];
     }
 }
diff --git a/src/Entities/MenuButton/MenuButtonNotImplemented.php b/src/Entities/MenuButton/MenuButtonNotImplemented.php
deleted file mode 100644
index 8db8ded05..000000000
--- a/src/Entities/MenuButton/MenuButtonNotImplemented.php
+++ /dev/null
@@ -1,8 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\MenuButton;
-
-class MenuButtonNotImplemented extends MenuButton
-{
-
-}
diff --git a/src/Entities/MenuButton/MenuButtonWebApp.php b/src/Entities/MenuButton/MenuButtonWebApp.php
index b82b128b9..bec75ae56 100644
--- a/src/Entities/MenuButton/MenuButtonWebApp.php
+++ b/src/Entities/MenuButton/MenuButtonWebApp.php
@@ -1,30 +1,26 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MenuButton;
+namespace PhpTelegramBot\Core\Entities\MenuButton;
 
-use Longman\TelegramBot\Entities\WebAppInfo;
+use PhpTelegramBot\Core\Entities\WebAppInfo;
 
 /**
- * Represents a menu button, which launches a Web App.
- *
  * @method string     getText()   Text on the button
- * @method WebAppInfo getWebApp() Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery.
- *
- * @method $this setText(string $text)          Text on the button
- * @method $this setWebApp(WebAppInfo $web_app) Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery.
+ * @method WebAppInfo getWebApp() Description of the Web App that will be launched when the user presses the button. The Web App will be able to send an arbitrary message on behalf of the user using the method answerWebAppQuery. Alternatively, a t.me link to a Web App of the bot can be specified in the object instead of the Web App's URL, in which case the Web App will be opened as if the user pressed the link.
  */
 class MenuButtonWebApp extends MenuButton
 {
-    public function __construct(array $data)
+    protected static function subEntities(): array
     {
-        $data['type'] = 'web_app';
-        parent::__construct($data);
+        return [
+            'web_app' => WebAppInfo::class,
+        ];
     }
 
-    protected function subEntities(): array
+    protected static function presetData(): array
     {
         return [
-            'web_app' => WebAppInfo::class,
+            'type' => self::TYPE_WEB_APP,
         ];
     }
 }
diff --git a/src/Entities/Message.php b/src/Entities/Message.php
index 8f771a305..b8c0b6cbe 100644
--- a/src/Entities/Message.php
+++ b/src/Entities/Message.php
@@ -1,137 +1,117 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-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;
-use Longman\TelegramBot\Entities\Topics\ForumTopicClosed;
-use Longman\TelegramBot\Entities\Topics\ForumTopicCreated;
-use Longman\TelegramBot\Entities\Topics\ForumTopicEdited;
-use Longman\TelegramBot\Entities\Topics\ForumTopicReopened;
-use Longman\TelegramBot\Entities\Topics\GeneralForumTopicHidden;
-use Longman\TelegramBot\Entities\Topics\GeneralForumTopicUnhidden;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+use PhpTelegramBot\Core\Entities\MessageOrigin\MessageOrigin;
 
 /**
- * Class Message
- *
- * Represents a message
- *
- * @link https://core.telegram.org/bots/api#message
- *
- * @method int                                    getMessageId()                              Unique message identifier
- * @method int                                    getMessageThreadId()                        Optional. Unique identifier of a message thread to which the message belongs; for supergroups only
- * @method User                                   getFrom()                                   Optional. Sender, can be empty for messages sent to channels
- * @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 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
- * @method Document                               getDocument()                               Optional. Message is a general file, information about the file
- * @method Animation                              getAnimation()                              Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set
- * @method Game                                   getGame()                                   Optional. Message is a game, information about the game.
- * @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 Voice                                  getVoice()                                  Optional. Message is a voice message, information about the file
- * @method VideoNote                              getVideoNote()                              Optional. Message is a video note message, information about the video
- * @method string                                 getCaption()                                Optional. Caption for the document, photo or video, 0-200 characters
- * @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 Location                               getLocation()                               Optional. Message is a shared location, information about the location
- * @method Venue                                  getVenue()                                  Optional. Message is a venue, information about the venue
- * @method Poll                                   getPoll()                                   Optional. Message is a native poll, information about the poll
- * @method Dice                                   getDice()                                   Optional. Message is a dice with random value, 1-6 for “🎲” and “🎯” base emoji, 1-5 for “🏀” and “⚽” base emoji, 1-64 for “🎰” base emoji
- * @method User[]                                 getNewChatMembers()                         Optional. A new member(s) was added to the group, information about them (one of this members may be the bot itself)
- * @method User                                   getLeftChatMember()                         Optional. A member was removed from the group, information about them (this member may be the bot itself)
- * @method string                                 getNewChatTitle()                           Optional. A chat title was changed to this value
- * @method PhotoSize[]                            getNewChatPhoto()                           Optional. A chat photo was changed to this value
- * @method MessageAutoDeleteTimerChanged          getMessageAutoDeleteTimerChanged()          Optional. Service message: auto-delete timer settings changed in the chat
- * @method bool                                   getDeleteChatPhoto()                        Optional. Service message: the chat photo was deleted
- * @method bool                                   getGroupChatCreated()                       Optional. Service message: the group has been created
- * @method bool                                   getSupergroupChatCreated()                  Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can’t be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.
- * @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 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 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
- * @method PassportData                           getPassportData()                           Optional. Telegram Passport data
- * @method ProximityAlertTriggered                getProximityAlertTriggered()                Optional. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location.
- * @method ForumTopicCreated                      getForumTopicCreated()                      Optional. Service message: forum topic created
- * @method ForumTopicEdited                       getForumTopicEdited()                       Optional. Service message: forum topic edited
- * @method ForumTopicClosed                       getForumTopicClosed()                       Optional. Service message: forum topic closed
- * @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
- * @method VideoChatParticipantsInvited           getVideoChatParticipantsInvited()           Optional. Service message: new participants invited to a voice chat
- * @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.
+ * @method int                                getMessageId()                     Unique message identifier inside this chat
+ * @method int|null                           getMessageThreadId()               Optional. Unique identifier of a message thread to which the message belongs; for supergroups only
+ * @method User|null                          getFrom()                          Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the field contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat.
+ * @method Chat|null                          getSenderChat()                    Optional. Sender of the message, sent on behalf of a chat. For example, the channel itself for channel posts, the supergroup itself for messages from anonymous group administrators, the linked channel for messages automatically forwarded to the discussion group. For backward compatibility, the field from contains a fake sender user in non-channel chats, if the message was sent on behalf of a chat.
+ * @method int|null                           getSenderBoostCount()              Optional. If the sender of the message boosted the chat, the number of boosts added by the user
+ * @method User|null                          getSenderBusinessBot()             Optional. The bot that actually sent the message on behalf of the business account. Available only for outgoing messages sent on behalf of the connected business account.
+ * @method int                                getDate()                          Date the message was sent in Unix time. It is always a positive number, representing a valid date.
+ * @method string|null                        getBusinessConnectionId()          Optional. Unique identifier of the business connection from which the message was received. If non-empty, the message belongs to a chat of the corresponding business account that is independent from any potential bot chat which might share the same identifier.
+ * @method Chat                               getChat()                          Chat the message belongs to
+ * @method MessageOrigin|null                 getForwardOrigin()                 Optional. Information about the original message for forwarded messages
+ * @method bool                               isTopicMessage()                   Optional. True, if the message is sent to a forum topic
+ * @method bool                               isAutomaticForward()               Optional. True, if the message is a channel post that was automatically forwarded to the connected discussion group
+ * @method Message|null                       getReplyToMessage()                Optional. For replies in the same chat and message thread, 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|null             getExternalReply()                 Optional. Information about the message that is being replied to, which may come from another chat or forum topic
+ * @method TextQuote|null                     getQuote()                         Optional. For replies that quote part of the original message, the quoted part of the message
+ * @method Story|null                         getReplyToStory()                  Optional. For replies to a story, the original story
+ * @method User|null                          getViaBot()                        Optional. Bot through which the message was sent
+ * @method int|null                           getEditDate()                      Optional. Date the message was last edited in Unix time
+ * @method bool                               hasProtectedContent()              Optional. True, if the message can't be forwarded
+ * @method bool                               isFromOffline()                    Optional. True, if the message was sent by an implicit action, for example, as an away or a greeting business message, or as a scheduled message
+ * @method string|null                        getMediaGroupId()                  Optional. The unique identifier of a media message group this message belongs to
+ * @method string|null                        getAuthorSignature()               Optional. Signature of the post author for messages in channels, or the custom title of an anonymous group administrator
+ * @method string|null                        getText()                          Optional. For text messages, the actual UTF-8 text of the message
+ * @method MessageEntity[]|null               getEntities()                      Optional. For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text
+ * @method LinkPreviewOptions|null            getLinkPreviewOptions()            Optional. Options used for link preview generation for the message, if it is a text message and link preview options were changed
+ * @method string|null                        getEffectId()                      Optional. Unique identifier of the message effect added to the message
+ * @method Animation|null                     getAnimation()                     Optional. Message is an animation, information about the animation. For backward compatibility, when this field is set, the document field will also be set
+ * @method Audio|null                         getAudio()                         Optional. Message is an audio file, information about the file
+ * @method Document|null                      getDocument()                      Optional. Message is a general file, information about the file
+ * @method PaidMediaInfo|null                 getPaidMedia()                     Optional. Message contains paid media; information about the paid media
+ * @method PhotoSize[]|null                   getPhoto()                         Optional. Message is a photo, available sizes of the photo
+ * @method Sticker|null                       getSticker()                       Optional. Message is a sticker, information about the sticker
+ * @method Story|null                         getStory()                         Optional. Message is a forwarded story
+ * @method Video|null                         getVideo()                         Optional. Message is a video, information about the video
+ * @method VideoNote|null                     getVideoNote()                     Optional. Message is a video note, information about the video message
+ * @method Voice|null                         getVoice()                         Optional. Message is a voice message, information about the file
+ * @method string|null                        getCaption()                       Optional. Caption for the animation, audio, document, photo, video or voice
+ * @method MessageEntity[]|null               getCaptionEntities()               Optional. For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption
+ * @method true|null                          getShowCaptionAboveMedia()         Optional. True, if the caption must be shown above the message media
+ * @method bool                               hasMediaSpoiler()                  Optional. True, if the message media is covered by a spoiler animation
+ * @method Contact|null                       getContact()                       Optional. Message is a shared contact, information about the contact
+ * @method Dice|null                          getDice()                          Optional. Message is a dice with random value
+ * @method Game|null                          getGame()                          Optional. Message is a game, information about the game.
+ * @method Poll|null                          getPoll()                          Optional. Message is a native poll, information about the poll
+ * @method Venue|null                         getVenue()                         Optional. Message is a venue, information about the venue. For backward compatibility, when this field is set, the location field will also be set
+ * @method Location|null                      getLocation()                      Optional. Message is a shared location, information about the location
+ * @method User[]|null                        getNewChatMembers()                Optional. New members that were added to the group or supergroup and information about them (the bot itself may be one of these members).
+ * @method User|null                          getLeftChatMember()                Optional. A member was removed from the group, information about them (this member may be the bot itself).
+ * @method string|null                        getNewChatTitle()                  Optional. A chat title was changed to this value
+ * @method PhotoSize[]|null                   getNewChatPhoto()                  Optional. A chat photo was change to this value
+ * @method true|null                          getDeleteChatPhoto()               Optional. Service message: the chat photo was deleted
+ * @method true|null                          getGroupChatCreated()              Optional. Service message: the group has been created
+ * @method true|null                          getSupergroupChatCreated()         Optional. Service message: the supergroup has been created. This field can't be received in a message coming through updates, because bot can't be a member of a supergroup when it is created. It can only be found in reply_to_message if someone replies to a very first message in a directly created supergroup.
+ * @method true|null                          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 MessageAutoDeleteTimerChanged|null getMessageAutoDeleteTimerChanged() Optional. Service message: auto-delete timer settings changed in the chat
+ * @method int|null                           getMigrateToChatId()               Optional. The group has been migrated to a supergroup with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method int|null                           getMigrateFromChatId()             Optional. The supergroup has been migrated from a group with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method MaybeInaccessibleMessage|null      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 itself is a reply.
+ * @method Invoice|null                       getInvoice()                       Optional. Message is an invoice for a payment, information about the invoice.
+ * @method SuccessfulPayment|null             getSuccessfulPayment()             Optional. Message is a service message about a successful payment, information about the payment.
+ * @method UsersShared|null                   getUsersShared()                   Optional. Service message: users were shared with the bot
+ * @method ChatShared|null                    getChatShared()                    Optional. Service message: a chat was shared with the bot
+ * @method string|null                        getConnectedWebsite()              Optional. The domain name of the website on which the user has logged in.
+ * @method WriteAccessAllowed|null            getWriteAccessAllowed()            Optional. Service message: the user allowed the bot to write messages after adding it to the attachment or side menu, launching a Web App from a link, or accepting an explicit request from a Web App sent by the method requestWriteAccess
+ * @method PassportData|null                  getPassportData()                  Optional. Telegram Passport data
+ * @method ProximityAlertTriggered|null       getProximityAlertTriggered()       Optional. Service message. A user in the chat triggered another user's proximity alert while sharing Live Location.
+ * @method ChatBoostAdded|null                getBoostAdded()                    Optional. Service message: user boosted the chat
+ * @method ChatBackground|null                getChatBackgroundSet()             Optional. Service message: chat background set
+ * @method ForumTopicCreated|null             getForumTopicCreated()             Optional. Service message: forum topic created
+ * @method ForumTopicEdited|null              getForumTopicEdited()              Optional. Service message: forum topic edited
+ * @method ForumTopicClosed|null              getForumTopicClosed()              Optional. Service message: forum topic closed
+ * @method ForumTopicReopened|null            getForumTopicReopened()            Optional. Service message: forum topic reopened
+ * @method GeneralForumTopicHidden|null       getGeneralForumTopicHidden()       Optional. Service message: the 'General' forum topic hidden
+ * @method GeneralForumTopicUnhidden|null     getGeneralForumTopicUnhidden()     Optional. Service message: the 'General' forum topic unhidden
+ * @method GiveawayCreated|null               getGiveawayCreated()               Optional. Service message: a scheduled giveaway was created
+ * @method Giveaway|null                      getGiveaway()                      Optional. The message is a scheduled giveaway message
+ * @method GiveawayWinners|null               getGiveawayWinners()               Optional. A giveaway with public winners was completed
+ * @method GiveawayCompleted|null             getGiveawayCompleted()             Optional. Service message: a giveaway without public winners was completed
+ * @method VideoChatScheduled|null            getVideoChatScheduled()            Optional. Service message: video chat scheduled
+ * @method VideoChatStarted|null              getVideoChatStarted()              Optional. Service message: video chat started
+ * @method VideoChatEnded|null                getVideoChatEnded()                Optional. Service message: video chat ended
+ * @method VideoChatParticipantsInvited|null  getVideoChatParticipantsInvited()  Optional. Service message: new participants invited to a video chat
+ * @method WebAppData|null                    getWebAppData()                    Optional. Service message: data sent by a Web App
+ * @method InlineKeyboardMarkup               getReplyMarkup()                   Optional. Inline keyboard attached to the message. login_url buttons are represented as ordinary url buttons.
  */
-class Message extends Entity implements MaybeInaccessibleMessage
+class Message extends MaybeInaccessibleMessage implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'from'                              => User::class,
             'sender_chat'                       => Chat::class,
+            'sender_business_bot'               => User::class,
             'chat'                              => Chat::class,
-            'forward_origin'                    => MessageOriginFactory::class,
-            'reply_to_message'                  => ReplyToMessage::class,
+            'forward_origin'                    => MessageOrigin::class,
+            'reply_to_message'                  => Message::class,
             'external_reply'                    => ExternalReplyInfo::class,
             'quote'                             => TextQuote::class,
+            'reply_to_story'                    => Story::class,
             'via_bot'                           => User::class,
-            'link_preview_options'              => LinkPreviewOptions::class,
             'entities'                          => [MessageEntity::class],
+            'link_preview_options'              => LinkPreviewOptions::class,
             'animation'                         => Animation::class,
             'audio'                             => Audio::class,
             'document'                          => Document::class,
+            'paid_media'                        => PaidMediaInfo::class,
             'photo'                             => [PhotoSize::class],
             'sticker'                           => Sticker::class,
             'story'                             => Story::class,
@@ -149,7 +129,7 @@ protected function subEntities(): array
             'left_chat_member'                  => User::class,
             'new_chat_photo'                    => [PhotoSize::class],
             'message_auto_delete_timer_changed' => MessageAutoDeleteTimerChanged::class,
-            'pinned_message'                    => MaybeInaccessibleMessageFactory::class,
+            'pinned_message'                    => MaybeInaccessibleMessage::class,
             'invoice'                           => Invoice::class,
             'successful_payment'                => SuccessfulPayment::class,
             'users_shared'                      => UsersShared::class,
@@ -157,6 +137,8 @@ protected function subEntities(): array
             'write_access_allowed'              => WriteAccessAllowed::class,
             'passport_data'                     => PassportData::class,
             'proximity_alert_triggered'         => ProximityAlertTriggered::class,
+            'boost_added'                       => ChatBoostAdded::class,
+            'chat_background_set'               => ChatBackground::class,
             'forum_topic_created'               => ForumTopicCreated::class,
             'forum_topic_edited'                => ForumTopicEdited::class,
             'forum_topic_closed'                => ForumTopicClosed::class,
@@ -172,166 +154,18 @@ protected function subEntities(): array
             'video_chat_ended'                  => VideoChatEnded::class,
             'video_chat_participants_invited'   => VideoChatParticipantsInvited::class,
             'web_app_data'                      => WebAppData::class,
-            'reply_markup'                      => InlineKeyboard::class,
+            'reply_markup'                      => InlineKeyboardMarkup::class,
         ];
     }
 
-    /**
-     * return the entire command like /echo or /echo@bot1 if specified
-     *
-     * @return string|null
-     */
-    public function getFullCommand(): ?string
-    {
-        $text = $this->getProperty('text') ?? '';
-        if (strpos($text, '/') !== 0) {
-            return null;
-        }
-
-        $no_EOL = strtok($text, PHP_EOL);
-        $no_space = strtok($text, ' ');
-
-        //try to understand which separator \n or space divide /command from text
-        return strlen($no_space) < strlen($no_EOL) ? $no_space : $no_EOL;
-    }
-
-    /**
-     * Get command
-     *
-     * @return string|null
-     */
-    public function getCommand(): ?string
+    public static function fieldsBypassingGet(): array
     {
-        if ($command = $this->getProperty('command')) {
-            return $command;
-        }
-
-        $full_command = $this->getFullCommand() ?? '';
-        if (strpos($full_command, '/') !== 0) {
-            return null;
-        }
-        $full_command = substr($full_command, 1);
-
-        //check if command is followed by bot username
-        $split_cmd = explode('@', $full_command);
-        if (! isset($split_cmd[1])) {
-            //command is not followed by name
-            return $full_command;
-        }
-
-        if (strtolower($split_cmd[1]) === strtolower($this->getBotUsername())) {
-            //command is addressed to me
-            return $split_cmd[0];
-        }
-
-        return null;
-    }
-
-    /**
-     * For text messages, the actual UTF-8 text of the message, 0-4096 characters.
-     *
-     * @param  bool  $without_cmd
-     *
-     * @return string|null
-     */
-    public function getText($without_cmd = false): ?string
-    {
-        $text = $this->getProperty('text');
-
-        if ($without_cmd && $command = $this->getFullCommand()) {
-            if (strlen($command) + 1 < strlen($text)) {
-                return substr($text, strlen($command) + 1);
-            }
-
-            return '';
-        }
-
-        return $text;
-    }
-
-    /**
-     * Bot added in chat
-     *
-     * @return bool
-     */
-    public function botAddedInChat(): bool
-    {
-        foreach ($this->getNewChatMembers() as $member) {
-            if ($member instanceof User && $member->getUsername() === $this->getBotUsername()) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Detect type based on properties.
-     *
-     * @return string
-     */
-    public function getType(): string
-    {
-        $types = [
-            'text',
-            'animation',
-            'audio',
-            'document',
-            'photo',
-            'sticker',
-            'video',
-            'video_note',
-            'voice',
-            'contact',
-            'dice',
-            'game',
-            'poll',
-            'venue',
-            'location',
-            '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',
-            'users_shared',
-            'chat_shared',
-            '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',
+        return [
+            'is_topic_message'      => false,
+            'is_automatic_forward'  => false,
+            'has_protected_content' => false,
+            'is_from_offline'       => false,
+            'has_media_spoiler'     => false,
         ];
-
-        $is_command = $this->getCommand() !== null;
-        foreach ($types as $type) {
-            if ($this->getProperty($type) !== null) {
-                if ($is_command && $type === 'text') {
-                    return 'command';
-                }
-
-                return $type;
-            }
-        }
-
-        return 'message';
     }
 }
diff --git a/src/Entities/Message/Factory.php b/src/Entities/Message/Factory.php
deleted file mode 100644
index 63ce362ef..000000000
--- a/src/Entities/Message/Factory.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Message;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\Message;
-
-class Factory extends \Longman\TelegramBot\Entities\Factory
-{
-
-    public static function make(array $data, string $bot_username): Entity
-    {
-        if ($data['date'] === 0) {
-            $class = InaccessibleMessage::class;
-        } else {
-            $class = Message::class;
-        }
-
-        return new $class($data, $bot_username);
-    }
-}
diff --git a/src/Entities/Message/InaccessibleMessage.php b/src/Entities/Message/InaccessibleMessage.php
deleted file mode 100644
index 9b5f27482..000000000
--- a/src/Entities/Message/InaccessibleMessage.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Message;
-
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * @method Chat getChat()      Chat the message belonged to
- * @method int  getMessageId() Unique message identifier inside the chat
- * @method int  getDate()      Always 0. The field can be used to differentiate regular and inaccessible messages.
- */
-class InaccessibleMessage extends Entity implements MaybeInaccessibleMessage
-{
-    protected function subEntities(): array
-    {
-        return [
-            'chat' => Chat::class,
-        ];
-    }
-}
diff --git a/src/Entities/Message/MaybeInaccessibleMessage.php b/src/Entities/Message/MaybeInaccessibleMessage.php
deleted file mode 100644
index 8e9d1b85c..000000000
--- a/src/Entities/Message/MaybeInaccessibleMessage.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Message;
-
-use Longman\TelegramBot\Entities\Chat;
-
-/**
- * @method Chat getChat()      Chat the message belonged to
- * @method int  getMessageId() Unique message identifier inside this chat
- * @method int  getDate()      The field can be used to differentiate regular and inaccessible messages.
- */
-interface MaybeInaccessibleMessage
-{
-    //
-}
diff --git a/src/Entities/MessageAutoDeleteTimerChanged.php b/src/Entities/MessageAutoDeleteTimerChanged.php
index 88b180384..a84476007 100644
--- a/src/Entities/MessageAutoDeleteTimerChanged.php
+++ b/src/Entities/MessageAutoDeleteTimerChanged.php
@@ -1,26 +1,11 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class MessageAutoDeleteTimerChanged
- *
- * Represents a service message about a change in auto-delete timer settings
- *
- * @link https://core.telegram.org/bots/api#messageautodeletetimerchanged
- *
- * @method int        getMessageAutoDeleteTime()       New auto-delete time for messages in the chat
+ * @method int getMessageAutoDeleteTime() New auto-delete time for messages in the chat; in seconds
  */
 class MessageAutoDeleteTimerChanged extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/MessageEntity.php b/src/Entities/MessageEntity.php
index d09ab4684..0890495ea 100644
--- a/src/Entities/MessageEntity.php
+++ b/src/Entities/MessageEntity.php
@@ -1,35 +1,57 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class MessageEntity
- *
- * @link https://core.telegram.org/bots/api#messageentity
- *
- * @method string getType()          Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” ($USD), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers)
- * @method int    getOffset()        Offset in UTF-16 code units to the start of the entity
- * @method int    getLength()        Length of the entity in UTF-16 code units
- * @method string getUrl()           Optional. For "text_link" only, url that will be opened after user taps on the text
- * @method User   getUser()          Optional. For "text_mention" only, the mentioned user
- * @method string getLanguage()      Optional. For "pre" only, the programming language of the entity text
- * @method string getCustomEmojiId() Optional. For “custom_emoji” only, unique identifier of the custom emoji. Use getCustomEmojiStickers to get full information about the sticker
+ * @method string      getType() Type of the entity. Currently, can be “mention” (@username), “hashtag” (#hashtag), “cashtag” ($USD), “bot_command” (/start@jobs_bot), “url” (https://telegram.org), “email” (do-not-reply@telegram.org), “phone_number” (+1-212-555-0123), “bold” (bold text), “italic” (italic text), “underline” (underlined text), “strikethrough” (strikethrough text), “spoiler” (spoiler message), “blockquote” (block quotation), “expandable_blockquote” (collapsed-by-default block quotation), “code” (monowidth string), “pre” (monowidth block), “text_link” (for clickable text URLs), “text_mention” (for users without usernames), “custom_emoji” (for inline custom emoji stickers)
+ * @method int         getOffset()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Offset in UTF-16 code units to the start of the entity
+ * @method int         getLength()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               Length of the entity in UTF-16 code units
+ * @method string|null getUrl()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  Optional. For “text_link” only, URL that will be opened after user taps on the text
+ * @method User|null   getUser()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 Optional. For “text_mention” only, the mentioned user
+ * @method string|null getLanguage()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             Optional. For “pre” only, the programming language of the entity text
+ * @method string|null getCustomEmojiId()                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        Optional. For “custom_emoji” only, unique identifier of the custom emoji. Use getCustomEmojiStickers to get full information about the sticker
  */
 class MessageEntity extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    public const TYPE_MENTION = 'mention'; // @username
+
+    public const TYPE_HASHTAG = 'hashtag'; // #hashtag
+
+    public const TYPE_CASHTAG = 'cashtag'; // $USD
+
+    public const TYPE_BOT_COMMAND = 'bot_command'; // /start@jobs_bot
+
+    public const TYPE_URL = 'url'; // https://telegram.org
+
+    public const TYPE_EMAIL = 'email'; // do-not-reply@telegram.org
+
+    public const TYPE_PHONE_NUMBER = 'phone_number'; // +1-212-555-0123
+
+    public const TYPE_BOLD = 'bold'; // bold text
+
+    public const TYPE_ITALIC = 'italic'; // italic text
+
+    public const TYPE_UNDERLINE = 'underline'; // underlined text
+
+    public const TYPE_STRIKETHROUGH = 'strikethrough'; // strikethrough text
+
+    public const TYPE_SPOILER = 'spoiler'; // spoiler message
+
+    public const TYPE_BLOCKQUOTE = 'blockquote'; // block quotation
+
+    public const TYPE_EXPANDABLE_BLOCKQUOTE = 'expandable_blockquote'; // collapsed-by-default block quotation
+
+    public const TYPE_CODE = 'code'; // monowidth string
+
+    public const TYPE_PRE = 'pre'; // monowidth block
+
+    public const TYPE_TEXT_LINK = 'text_link'; // for clickable text URLs
+
+    public const TYPE_TEXT_MENTION = 'text_mention'; // for users without usernames
+
+    public const TYPE_CUSTOM_EMOJI = 'custom_emoji'; // for inline custom emoji stickers
+
+    protected static function subEntities(): array
     {
         return [
             'user' => User::class,
diff --git a/src/Entities/MessageId.php b/src/Entities/MessageId.php
new file mode 100644
index 000000000..2ca56e183
--- /dev/null
+++ b/src/Entities/MessageId.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int getMessageId() Unique message identifier
+ */
+class MessageId extends Entity
+{
+    //
+}
diff --git a/src/Entities/MessageOrigin/Factory.php b/src/Entities/MessageOrigin/Factory.php
deleted file mode 100644
index d32205062..000000000
--- a/src/Entities/MessageOrigin/Factory.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\MessageOrigin;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class Factory extends \Longman\TelegramBot\Entities\Factory
-{
-    public static function make(array $data, string $bot_username): Entity
-    {
-        $type = [
-            'user'        => 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
index a8a468c0f..0ea975b51 100644
--- a/src/Entities/MessageOrigin/MessageOrigin.php
+++ b/src/Entities/MessageOrigin/MessageOrigin.php
@@ -1,14 +1,23 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MessageOrigin;
+namespace PhpTelegramBot\Core\Entities\MessageOrigin;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
 
 /**
- * This object describes the origin of a message.
- *
  * @method string getType() Type of the message origin
  * @method int    getDate() Date the message was sent originally in Unix time
  */
-interface MessageOrigin
+abstract class MessageOrigin extends Entity implements Factory
 {
-    //
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            'user'        => new MessageOriginUser($data),
+            'hidden_user' => new MessageOriginHiddenUser($data),
+            'chat'        => new MessageOriginChat($data),
+            'channel'     => new MessageOriginChannel($data),
+        };
+    }
 }
diff --git a/src/Entities/MessageOrigin/MessageOriginChannel.php b/src/Entities/MessageOrigin/MessageOriginChannel.php
index acb9807f4..66afac5b5 100644
--- a/src/Entities/MessageOrigin/MessageOriginChannel.php
+++ b/src/Entities/MessageOrigin/MessageOriginChannel.php
@@ -1,24 +1,17 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MessageOrigin;
+namespace PhpTelegramBot\Core\Entities\MessageOrigin;
 
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Entities\Chat;
 
 /**
- * The message was originally sent to a channel chat.
- *
- * @link https://core.telegram.org/bots/api#messageoriginchannel
- *
- * @method string getType()            Type of the message origin, always “channel”
- * @method int    getDate()            Date the message was sent originally in Unix time
- * @method Chat   getChat()            Channel chat to which the message was originally sent
- * @method int    getMessageId()       Unique message identifier inside the chat
- * @method string getAuthorSignature() Optional. Signature of the original post author
+ * @method Chat        getChat()            Channel chat to which the message was originally sent
+ * @method int         getMessageId()       Unique message identifier inside the chat
+ * @method string|null getAuthorSignature() Optional. Signature of the original post author
  */
-class MessageOriginChannel extends Entity implements MessageOrigin
+class MessageOriginChannel extends MessageOrigin
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'chat' => Chat::class,
diff --git a/src/Entities/MessageOrigin/MessageOriginChat.php b/src/Entities/MessageOrigin/MessageOriginChat.php
index a0c2b398b..c5a5b0f61 100644
--- a/src/Entities/MessageOrigin/MessageOriginChat.php
+++ b/src/Entities/MessageOrigin/MessageOriginChat.php
@@ -1,23 +1,16 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MessageOrigin;
+namespace PhpTelegramBot\Core\Entities\MessageOrigin;
 
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Entity;
+use PhpTelegramBot\Core\Entities\Chat;
 
 /**
- * The message was originally sent on behalf of a chat to a group chat.
- *
- * @link https://core.telegram.org/bots/api#messageoriginchat
- *
- * @method string getType()            Type of the message origin, always “chat”
- * @method int    getDate()            Date the message was sent originally in Unix time
- * @method Chat   getChat()            Chat that sent the message originally
- * @method string getAuthorSignature() Optional. For messages originally sent by an anonymous chat administrator, original message author signature
+ * @method Chat        getSenderChat()      Chat that sent the message originally
+ * @method string|null getAuthorSignature() Optional. For messages originally sent by an anonymous chat administrator, original message author signature
  */
-class MessageOriginChat extends Entity implements MessageOrigin
+class MessageOriginChat extends MessageOrigin
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'sender_chat' => Chat::class,
diff --git a/src/Entities/MessageOrigin/MessageOriginHiddenUser.php b/src/Entities/MessageOrigin/MessageOriginHiddenUser.php
index baff892f0..935dd8c0f 100644
--- a/src/Entities/MessageOrigin/MessageOriginHiddenUser.php
+++ b/src/Entities/MessageOrigin/MessageOriginHiddenUser.php
@@ -1,19 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MessageOrigin;
-
-use Longman\TelegramBot\Entities\Entity;
+namespace PhpTelegramBot\Core\Entities\MessageOrigin;
 
 /**
- * The message was originally sent by an unknown user.
- *
- * @link https://core.telegram.org/bots/api#messageoriginhiddenuser
- *
- * @method string getType()           Type of the message origin, always “hidden_user”
- * @method int    getDate()           Date the message was sent originally in Unix time
  * @method string getSenderUserName() Name of the user that sent the message originally
  */
-class MessageOriginHiddenUser extends Entity implements MessageOrigin
+class MessageOriginHiddenUser extends MessageOrigin
 {
     //
 }
diff --git a/src/Entities/MessageOrigin/MessageOriginNotImplemented.php b/src/Entities/MessageOrigin/MessageOriginNotImplemented.php
deleted file mode 100644
index 7e9793e96..000000000
--- a/src/Entities/MessageOrigin/MessageOriginNotImplemented.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\MessageOrigin;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * @method string getType() Type of the message origin
- * @method int    getDate() Date the message was sent originally in Unix time
- */
-class MessageOriginNotImplemented extends Entity implements MessageOrigin
-{
-
-}
diff --git a/src/Entities/MessageOrigin/MessageOriginUser.php b/src/Entities/MessageOrigin/MessageOriginUser.php
index 9aa000ac2..16c7ae351 100644
--- a/src/Entities/MessageOrigin/MessageOriginUser.php
+++ b/src/Entities/MessageOrigin/MessageOriginUser.php
@@ -1,22 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\MessageOrigin;
+namespace PhpTelegramBot\Core\Entities\MessageOrigin;
 
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\User;
+use PhpTelegramBot\Core\Entities\User;
 
 /**
- * The message was originally sent by a known user.
- *
- * @link https://core.telegram.org/bots/api#messageoriginuser
- *
- * @method string getType()       Type of the message origin, always “user”
- * @method int    getDate()       Date the message was sent originally in Unix time
- * @method User   getSenderUser() User that sent the message originally
+ * @method User getSenderUser() User that sent the message originally
  */
-class MessageOriginUser extends Entity implements MessageOrigin
+class MessageOriginUser extends MessageOrigin
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'sender_user' => User::class,
diff --git a/src/Entities/MessageReactionCountUpdated.php b/src/Entities/MessageReactionCountUpdated.php
index 21dc848bd..35d0f8db6 100644
--- a/src/Entities/MessageReactionCountUpdated.php
+++ b/src/Entities/MessageReactionCountUpdated.php
@@ -1,12 +1,8 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * This object represents reaction changes on a message with anonymous reactions.
- *
- * @link https://core.telegram.org/bots/api#messagereactioncountupdated
- *
  * @method Chat            getChat()      The chat containing the message
  * @method int             getMessageId() Unique message identifier inside the chat
  * @method int             getDate()      Date of the change in Unix time
@@ -14,7 +10,7 @@
  */
 class MessageReactionCountUpdated extends Entity
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'chat'      => Chat::class,
diff --git a/src/Entities/MessageReactionUpdated.php b/src/Entities/MessageReactionUpdated.php
index c5b602147..bb7043869 100644
--- a/src/Entities/MessageReactionUpdated.php
+++ b/src/Entities/MessageReactionUpdated.php
@@ -1,33 +1,28 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
-use Longman\TelegramBot\Entities\ReactionType\Factory as ReactionTypeFactory;
-use Longman\TelegramBot\Entities\ReactionType\ReactionType;
+use PhpTelegramBot\Core\Entities\ReactionType\ReactionType;
 
 /**
- * This object represents a change of a reaction on a message performed by a user.
- *
- * @link https://core.telegram.org/bots/api#messagereactionupdated
- *
  * @method Chat           getChat()        The chat containing the message the user reacted to
  * @method int            getMessageId()   Unique identifier of the message inside the chat
- * @method User           getUser()        Optional. The user that changed the reaction, if the user isn't anonymous
- * @method Chat           getActorChat()   Optional. The chat on behalf of which the reaction was changed, if the user is anonymous
+ * @method User|null      getUser()        Optional. The user that changed the reaction, if the user isn't anonymous
+ * @method Chat|null      getActorChat()   Optional. The chat on behalf of which the reaction was changed, if the user is anonymous
  * @method int            getDate()        Date of the change in Unix time
  * @method ReactionType[] getOldReaction() Previous list of reaction types that were set by the user
  * @method ReactionType[] getNewReaction() New list of reaction types that have been set by the user
  */
 class MessageReactionUpdated extends Entity
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'chat'         => Chat::class,
             'user'         => User::class,
             'actor_chat'   => Chat::class,
-            'old_reaction' => [ReactionTypeFactory::class],
-            'new_reaction' => [ReactionTypeFactory::class],
+            'old_reaction' => [ReactionType::class],
+            'new_reaction' => [ReactionType::class],
         ];
     }
 }
diff --git a/src/Entities/OrderInfo.php b/src/Entities/OrderInfo.php
new file mode 100644
index 000000000..6a3050c34
--- /dev/null
+++ b/src/Entities/OrderInfo.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string|null          getName()            Optional. User name
+ * @method string|null          getPhoneNumber()     Optional. User's phone number
+ * @method string|null          getEmail()           Optional. User email
+ * @method ShippingAddress|null getShippingAddress() Optional. User shipping address
+ */
+class OrderInfo extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'shipping_address' => ShippingAddress::class,
+        ];
+    }
+}
diff --git a/src/Entities/PaidMedia/PaidMedia.php b/src/Entities/PaidMedia/PaidMedia.php
new file mode 100644
index 000000000..30d01e95d
--- /dev/null
+++ b/src/Entities/PaidMedia/PaidMedia.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\PaidMedia;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the paid media
+ */
+abstract class PaidMedia extends Entity implements Factory
+{
+    public const TYPE_PREVIEW = 'preview';
+
+    public const TYPE_PHOTO = 'photo';
+
+    public const TYPE_VIDEO = 'video';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_PREVIEW => new PaidMediaPreview($data),
+            self::TYPE_PHOTO   => new PaidMediaPhoto($data),
+            self::TYPE_VIDEO   => new PaidMediaVideo($data),
+        };
+    }
+}
diff --git a/src/Entities/PaidMedia/PaidMediaPhoto.php b/src/Entities/PaidMedia/PaidMediaPhoto.php
new file mode 100644
index 000000000..ea5a0b239
--- /dev/null
+++ b/src/Entities/PaidMedia/PaidMediaPhoto.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\PaidMedia;
+
+use PhpTelegramBot\Core\Entities\PhotoSize;
+
+/**
+ * @method PhotoSize[] getPhoto() The photo
+ */
+class PaidMediaPhoto extends PaidMedia
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'photo' => [PhotoSize::class],
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_PHOTO,
+        ];
+    }
+}
diff --git a/src/Entities/PaidMedia/PaidMediaPreview.php b/src/Entities/PaidMedia/PaidMediaPreview.php
new file mode 100644
index 000000000..4753a30d5
--- /dev/null
+++ b/src/Entities/PaidMedia/PaidMediaPreview.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\PaidMedia;
+
+/**
+ * @method int getWidth()    Optional. Media width as defined by the sender
+ * @method int getHeight()   Optional. Media height as defined by the sender
+ * @method int getDuration() Optional. Duration of the media in seconds as defined by the sender
+ */
+class PaidMediaPreview extends PaidMedia
+{
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_PREVIEW,
+        ];
+    }
+}
diff --git a/src/Entities/PaidMedia/PaidMediaVideo.php b/src/Entities/PaidMedia/PaidMediaVideo.php
new file mode 100644
index 000000000..bc5f4bf0f
--- /dev/null
+++ b/src/Entities/PaidMedia/PaidMediaVideo.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\PaidMedia;
+
+use PhpTelegramBot\Core\Entities\Video;
+
+/**
+ * @method Video getVideo() The video
+ */
+class PaidMediaVideo extends PaidMedia
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'video' => Video::class,
+        ];
+    }
+
+    protected static function presetData(): array
+    {
+        return [
+            'type' => self::TYPE_VIDEO,
+        ];
+    }
+}
diff --git a/src/Entities/PaidMediaInfo.php b/src/Entities/PaidMediaInfo.php
new file mode 100644
index 000000000..40f727232
--- /dev/null
+++ b/src/Entities/PaidMediaInfo.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Entities\PaidMedia\PaidMedia;
+
+/**
+ * @method int         getStarCount() The number of Telegram Stars that must be paid to buy access to the media
+ * @method PaidMedia[] getPaidMedia() Information about the paid media
+ */
+class PaidMediaInfo extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'paid_media' => [PaidMedia::class],
+        ];
+    }
+}
diff --git a/src/Entities/PassportData.php b/src/Entities/PassportData.php
new file mode 100644
index 000000000..ed7758999
--- /dev/null
+++ b/src/Entities/PassportData.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method EncryptedPassportElement[] getData()        Array with information about documents and other Telegram Passport elements that was shared with the bot
+ * @method EncryptedCredentials       getCredentials() Encrypted credentials required to decrypt the data
+ */
+class PassportData extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'data'        => [EncryptedPassportElement::class],
+            'credentials' => EncryptedCredentials::class,
+        ];
+    }
+}
diff --git a/src/Entities/PassportFile.php b/src/Entities/PassportFile.php
new file mode 100644
index 000000000..28feed109
--- /dev/null
+++ b/src/Entities/PassportFile.php
@@ -0,0 +1,14 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int    getFileSize()     File size in bytes
+ * @method int    getFileDate()     Unix time when the file was uploaded
+ */
+class PassportFile extends Entity
+{
+    //
+}
diff --git a/src/Entities/Payments/Invoice.php b/src/Entities/Payments/Invoice.php
deleted file mode 100644
index b30ac5d37..000000000
--- a/src/Entities/Payments/Invoice.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class Invoice
- *
- * This object contains basic information about an invoice.
- *
- * @link https://core.telegram.org/bots/api#invoice
- *
- * @method string getTitle()          Product name
- * @method string getDescription()    Product description
- * @method string getStartParameter() Unique bot deep-linking parameter that can be used to generate this invoice
- * @method string getCurrency()       Three-letter ISO 4217 currency code
- * @method int    getTotalAmount()    Total price in the smallest units of the currency (integer, not float/double).
- **/
-class Invoice extends Entity
-{
-
-}
diff --git a/src/Entities/Payments/LabeledPrice.php b/src/Entities/Payments/LabeledPrice.php
deleted file mode 100644
index a78d647d3..000000000
--- a/src/Entities/Payments/LabeledPrice.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class LabeledPrice
- *
- * This object represents a portion of the price for goods or services.
- *
- * @link https://core.telegram.org/bots/api#labeledprice
- *
- * @method string getLabel()  Portion label
- * @method int    getAmount() Price of the product in the smallest units of the currency (integer, not float/double).
- **/
-class LabeledPrice extends Entity
-{
-
-}
diff --git a/src/Entities/Payments/OrderInfo.php b/src/Entities/Payments/OrderInfo.php
deleted file mode 100644
index e539d4927..000000000
--- a/src/Entities/Payments/OrderInfo.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class OrderInfo
- *
- * This object represents information about an order.
- *
- * @link https://core.telegram.org/bots/api#orderinfo
- *
- * @method string          getName()            Optional. User name
- * @method string          getPhoneNumber()     Optional. User's phone number
- * @method string          getEmail()           Optional. User email
- * @method ShippingAddress getShippingAddress() Optional. User shipping address
- **/
-class OrderInfo extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'shipping_address' => ShippingAddress::class,
-        ];
-    }
-}
diff --git a/src/Entities/Payments/PreCheckoutQuery.php b/src/Entities/Payments/PreCheckoutQuery.php
deleted file mode 100644
index b58357101..000000000
--- a/src/Entities/Payments/PreCheckoutQuery.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Entities\User;
-use Longman\TelegramBot\Request;
-
-/**
- * Class PreCheckoutQuery
- *
- * This object contains information about an incoming pre-checkout query.
- *
- * @link https://core.telegram.org/bots/api#precheckoutquery
- *
- * @method string    getId()               Unique query identifier
- * @method User      getFrom()             User who sent the query
- * @method string    getCurrency()         Three-letter ISO 4217 currency code
- * @method int       getTotalAmount()      Total price in the smallest units of the currency (integer, not float/double).
- * @method string    getInvoicePayload()   Bot specified invoice payload
- * @method string    getShippingOptionId() Optional. Identifier of the shipping option chosen by the user
- * @method OrderInfo getOrderInfo()        Optional. Order info provided by the user
- **/
-class PreCheckoutQuery extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'from'       => User::class,
-            'order_info' => OrderInfo::class,
-        ];
-    }
-
-    /**
-     * Answer this pre-checkout query.
-     *
-     * @param bool  $ok
-     * @param array $data
-     *
-     * @return ServerResponse
-     */
-    public function answer(bool $ok, array $data = []): ServerResponse
-    {
-        return Request::answerPreCheckoutQuery(array_merge([
-            'pre_checkout_query_id' => $this->getId(),
-            'ok'                    => $ok,
-        ], $data));
-    }
-}
diff --git a/src/Entities/Payments/ShippingAddress.php b/src/Entities/Payments/ShippingAddress.php
deleted file mode 100644
index e48f64e7f..000000000
--- a/src/Entities/Payments/ShippingAddress.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class ShippingAddress
- *
- * This object represents a shipping address.
- *
- * @link https://core.telegram.org/bots/api#shippingaddress
- *
- * @method string getCountryCode() ISO 3166-1 alpha-2 country code
- * @method string getState()       State, if applicable
- * @method string getCity()        City
- * @method string getStreetLine1() First line for the address
- * @method string getStreetLine2() Second line for the address
- * @method string getPostCode()    Address post code
- **/
-class ShippingAddress extends Entity
-{
-
-}
diff --git a/src/Entities/Payments/ShippingOption.php b/src/Entities/Payments/ShippingOption.php
deleted file mode 100644
index f49eaf4d9..000000000
--- a/src/Entities/Payments/ShippingOption.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class ShippingOption
- *
- * This object represents one shipping option.
- *
- * @link https://core.telegram.org/bots/api#shippingoption
- *
- * @method string         getId()     Shipping option identifier
- * @method string         getTitle()  Option title
- * @method LabeledPrice[] getPrices() List of price portions
- **/
-class ShippingOption extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'prices' => [LabeledPrice::class],
-        ];
-    }
-}
diff --git a/src/Entities/Payments/ShippingQuery.php b/src/Entities/Payments/ShippingQuery.php
deleted file mode 100644
index f096f6613..000000000
--- a/src/Entities/Payments/ShippingQuery.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Entities\User;
-use Longman\TelegramBot\Request;
-
-/**
- * Class ShippingQuery
- *
- * This object contains information about an incoming shipping query.
- *
- * @link https://core.telegram.org/bots/api#shippingquery
- *
- * @method string          getId()              Unique query identifier
- * @method User            getFrom()            User who sent the query
- * @method string          getInvoicePayload()  Bot specified invoice payload
- * @method ShippingAddress getShippingAddress() User specified shipping address
- **/
-class ShippingQuery extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'from'             => User::class,
-            'shipping_address' => ShippingAddress::class,
-        ];
-    }
-
-    /**
-     * Answer this shipping query.
-     *
-     * @param bool  $ok
-     * @param array $data
-     *
-     * @return ServerResponse
-     */
-    public function answer(bool $ok, array $data = []): ServerResponse
-    {
-        return Request::answerShippingQuery(array_merge([
-            'shipping_query_id' => $this->getId(),
-            'ok'                => $ok,
-        ], $data));
-    }
-}
diff --git a/src/Entities/Payments/SuccessfulPayment.php b/src/Entities/Payments/SuccessfulPayment.php
deleted file mode 100644
index 8f2b5335e..000000000
--- a/src/Entities/Payments/SuccessfulPayment.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\Payments;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class SuccessfulPayment
- *
- * This object contains basic information about a successful payment.
- *
- * @link https://core.telegram.org/bots/api#successfulpayment
- *
- * @method string    getCurrency()                Three-letter ISO 4217 currency code
- * @method int       getTotalAmount()             Total price in the smallest units of the currency (integer, not float/double).
- * @method string    getInvoicePayload()          Bot specified invoice payload
- * @method string    getShippingOptionId()        Optional. Identifier of the shipping option chosen by the user
- * @method OrderInfo getOrderInfo()               Optional. Order info provided by the user
- * @method string    getTelegramPaymentChargeId() Telegram payment identifier
- * @method string    getProviderPaymentChargeId() Provider payment identifier
- **/
-class SuccessfulPayment extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'order_info' => OrderInfo::class,
-        ];
-    }
-}
diff --git a/src/Entities/PhotoSize.php b/src/Entities/PhotoSize.php
index 21718d28f..1a70fe9a1 100644
--- a/src/Entities/PhotoSize.php
+++ b/src/Entities/PhotoSize.php
@@ -1,28 +1,15 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class PhotoSize
- *
- * @link https://core.telegram.org/bots/api#photosize
- *
- * @method string getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int    getWidth()        Photo width
- * @method int    getHeight()       Photo height
- * @method int    getFileSize()     Optional. File size
+ * @method string   getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string   getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int      getWidth()        Photo width
+ * @method int      getHeight()       Photo height
+ * @method int|null getFileSize()     Optional. File size in bytes
  */
 class PhotoSize extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/Poll.php b/src/Entities/Poll.php
index 35313bc00..ace697074 100644
--- a/src/Entities/Poll.php
+++ b/src/Entities/Poll.php
@@ -1,47 +1,45 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * Class Poll
- *
- * This entity contains information about a poll.
- *
- * @link https://core.telegram.org/bots/api#poll
- *
- * @method string          getId()                    Unique poll identifier
- * @method string          getQuestion()              Poll question, 1-255 characters
- * @method PollOption[]    getOptions()               List of poll options
- * @method int             getTotalVoterCount()       Total number of users that voted in the poll
- * @method bool            getIsClosed()              True, if the poll is closed
- * @method bool            getIsAnonymous()           True, if the poll is anonymous
- * @method string          getType()                  Poll type, currently can be “regular” or “quiz”
- * @method bool            getAllowsMultipleAnswers() True, if the poll allows multiple answers
- * @method int             getCorrectOptionId()       Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.
- * @method string          getExplanation()           Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters
- * @method MessageEntity[] getExplanationEntities()   Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the explanation
- * @method int             getOpenPeriod()            Optional. Amount of time in seconds the poll will be active after creation
- * @method int             getCloseDate()             Optional. Point in time (Unix timestamp) when the poll will be automatically closed
+ * @method string               getId()                    Unique poll identifier
+ * @method string               getQuestion()              Poll question, 1-300 characters
+ * @method MessageEntity[]|null getQuestionEntities()      Optional. Special entities that appear in the question. Currently, only custom emoji entities are allowed in poll questions
+ * @method PollOption[]         getOptions()               List of poll options
+ * @method int                  getTotalVoterCount()       Total number of users that voted in the poll
+ * @method bool                 isClosed()                 True, if the poll is closed
+ * @method bool                 isAnonymous()              True, if the poll is anonymous
+ * @method string               getType()                  Poll type, currently can be “regular” or “quiz”
+ * @method bool                 getAllowsMultipleAnswers() True, if the poll allows multiple answers
+ * @method int|null             getCorrectOptionId()       Optional. 0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded), by the bot or to the private chat with the bot.
+ * @method string|null          getExplanation()           Optional. Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters
+ * @method MessageEntity[]|null getExplanationEntities()   Optional. Special entities like usernames, URLs, bot commands, etc. that appear in the explanation
+ * @method int|null             getOpenPeriod()            Optional. Amount of time in seconds the poll will be active after creation
+ * @method int|null             getCloseDate()             Optional. Point in time (Unix timestamp), when the poll will be automatically closed
  */
-class Poll extends Entity
+class Poll extends Entity implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    public const TYPE_REGULAR = 'regular';
+
+    public const TYPE_QUIZ = 'quiz';
+
+    protected static function subEntities(): array
     {
         return [
+            'question_entities'    => [MessageEntity::class],
             'options'              => [PollOption::class],
             'explanation_entities' => [MessageEntity::class],
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_closed'    => false,
+            'is_anonymous' => false,
+        ];
+    }
 }
diff --git a/src/Entities/PollAnswer.php b/src/Entities/PollAnswer.php
index f55fe916f..1031d5ff8 100644
--- a/src/Entities/PollAnswer.php
+++ b/src/Entities/PollAnswer.php
@@ -1,34 +1,16 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class PollAnswer
- *
- * This entity represents an answer of a user in a non-anonymous poll.
- *
- * @link https://core.telegram.org/bots/api#pollanswer
- *
- * @method string getPollId()    Unique poll identifier
- * @method Chat   getVoterChat() Optional. The chat that changed the answer to the poll, if the voter is anonymous
- * @method User   getUser()      Optional. The user, who changed the answer to the poll
- * @method array  getOptionIds() 0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote.
+ * @method string    getPollId()    Unique poll identifier
+ * @method Chat|null getVoterChat() Optional. The chat that changed the answer to the poll, if the voter is anonymous
+ * @method User|null getUser()      Optional. The user that changed the answer to the poll, if the voter isn't anonymous
+ * @method int[]     getOptionIds() 0-based identifiers of chosen answer options. May be empty if the vote was retracted.
  */
 class PollAnswer extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'voter_chat' => Chat::class,
diff --git a/src/Entities/PollOption.php b/src/Entities/PollOption.php
index 8af5f978b..c1352098c 100644
--- a/src/Entities/PollOption.php
+++ b/src/Entities/PollOption.php
@@ -1,27 +1,18 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class PollOption
- *
- * This entity contains information about one answer option in a poll.
- *
- * @link https://core.telegram.org/bots/api#polloption
- *
- * @method string getText()       Option text, 1-100 characters
- * @method int    getVoterCount() Number of users that voted for this option
+ * @method string               getText()         Option text, 1-100 characters
+ * @method MessageEntity[]|null getTextEntities() Optional. Special entities that appear in the option text. Currently, only custom emoji entities are allowed in poll option texts
+ * @method int                  getVoterCount()   Number of users that voted for this option
  */
 class PollOption extends Entity
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'text_entities' => [MessageEntity::class],
+        ];
+    }
 }
diff --git a/src/Entities/PreCheckoutQuery.php b/src/Entities/PreCheckoutQuery.php
new file mode 100644
index 000000000..77d13e02d
--- /dev/null
+++ b/src/Entities/PreCheckoutQuery.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string         getId()               Unique query identifier
+ * @method User           getFrom()             User who sent the query
+ * @method string         getCurrency()         Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars
+ * @method int            getTotalAmount()      Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+ * @method string         getInvoicePayload()   Bot specified invoice payload
+ * @method string|null    getShippingOptionId() Optional. Identifier of the shipping option chosen by the user
+ * @method OrderInfo|null getOrderInfo()        Optional. Order information provided by the user
+ */
+class PreCheckoutQuery extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'from'       => User::class,
+            'order_info' => OrderInfo::class,
+        ];
+    }
+}
diff --git a/src/Entities/ProximityAlertTriggered.php b/src/Entities/ProximityAlertTriggered.php
index 6aa14e312..eaf0c885f 100644
--- a/src/Entities/ProximityAlertTriggered.php
+++ b/src/Entities/ProximityAlertTriggered.php
@@ -1,33 +1,15 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class ProximityAlertTriggered
- *
- * Represents the content of a service message, sent whenever a user in the chat triggers a proximity alert set by another user.
- *
- * @link https://core.telegram.org/bots/api#proximityalerttriggered
- *
  * @method User getTraveler() User that triggered the alert
  * @method User getWatcher()  User that set the alert
  * @method int  getDistance() The distance between the users
  */
 class ProximityAlertTriggered extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'traveler' => User::class,
diff --git a/src/Entities/ReactionCount.php b/src/Entities/ReactionCount.php
index c99e67d3d..dd32d35e4 100644
--- a/src/Entities/ReactionCount.php
+++ b/src/Entities/ReactionCount.php
@@ -1,24 +1,19 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
-use Longman\TelegramBot\Entities\ReactionType\Factory as ReactionTypeFactory;
-use Longman\TelegramBot\Entities\ReactionType\ReactionType;
+use PhpTelegramBot\Core\Entities\ReactionType\ReactionType;
 
 /**
- * Represents a reaction added to a message along with the number of times it was added.
- *
- * @link https://core.telegram.org/bots/api#reactioncount
- *
  * @method ReactionType getType()       Type of the reaction
  * @method int          getTotalCount() Number of times the reaction was added
  */
 class ReactionCount extends Entity
 {
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
-            'type' => ReactionTypeFactory::class,
+            'type' => ReactionType::class,
         ];
     }
 }
diff --git a/src/Entities/ReactionType/Factory.php b/src/Entities/ReactionType/Factory.php
deleted file mode 100644
index f61cf368b..000000000
--- a/src/Entities/ReactionType/Factory.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\ReactionType;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class Factory extends \Longman\TelegramBot\Entities\Factory
-{
-    public static function make(array $data, string $bot_username): Entity
-    {
-        $type = [
-            'emoji'        => 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
index 901f951de..77e38a401 100644
--- a/src/Entities/ReactionType/ReactionType.php
+++ b/src/Entities/ReactionType/ReactionType.php
@@ -1,15 +1,24 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ReactionType;
+namespace PhpTelegramBot\Core\Entities\ReactionType;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
 
 /**
- * Interface ReactionType
- *
- * @link https://core.telegram.org/bots/api#reactiontype
- *
  * @method string getType() Type of the reaction
  */
-interface ReactionType
+class ReactionType extends Entity implements Factory
 {
+    public const TYPE_EMOJI = 'emoji';
+
+    public const TYPE_CUSTOM_EMOJI = 'custom_emoji';
 
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_EMOJI        => new ReactionTypeEmoji($data),
+            self::TYPE_CUSTOM_EMOJI => new ReactionTypeCustomEmoji($data),
+        };
+    }
 }
diff --git a/src/Entities/ReactionType/ReactionTypeCustomEmoji.php b/src/Entities/ReactionType/ReactionTypeCustomEmoji.php
index 6a23e521c..8186f0ada 100644
--- a/src/Entities/ReactionType/ReactionTypeCustomEmoji.php
+++ b/src/Entities/ReactionType/ReactionTypeCustomEmoji.php
@@ -1,22 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ReactionType;
-
-use Longman\TelegramBot\Entities\Entity;
+namespace PhpTelegramBot\Core\Entities\ReactionType;
 
 /**
- * The reaction is based on a custom emoji.
- *
- * @link https://core.telegram.org/bots/api#reactiontypecustomemoji
- *
- * @method string getType()          Type of the reaction, always “custom_emoji”
  * @method string getCustomEmojiId() Custom emoji identifier
  */
-class ReactionTypeCustomEmoji extends Entity implements ReactionType
+class ReactionTypeCustomEmoji extends ReactionType
 {
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'custom_emoji';
-        parent::__construct($data);
-    }
+    //
 }
diff --git a/src/Entities/ReactionType/ReactionTypeEmoji.php b/src/Entities/ReactionType/ReactionTypeEmoji.php
index 504c7252c..2ae4f74be 100644
--- a/src/Entities/ReactionType/ReactionTypeEmoji.php
+++ b/src/Entities/ReactionType/ReactionTypeEmoji.php
@@ -1,22 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities\ReactionType;
-
-use Longman\TelegramBot\Entities\Entity;
+namespace PhpTelegramBot\Core\Entities\ReactionType;
 
 /**
- * The reaction is based on an emoji.
- *
- * @link https://core.telegram.org/bots/api#reactiontypeemoji
- *
- * @method string getType()  Type of the reaction, always “emoji”
- * @method string getEmoji() Reaction emoji.
+ * @method string getEmoji() Reaction emoji. Currently, it can be one of "👍", "👎", "❤", "🔥", "🥰", "👏", "😁", "🤔", "🤯", "😱", "🤬", "😢", "🎉", "🤩", "🤮", "💩", "🙏", "👌", "🕊", "🤡", "🥱", "🥴", "😍", "🐳", "❤‍🔥", "🌚", "🌭", "💯", "🤣", "⚡", "🍌", "🏆", "💔", "🤨", "😐", "🍓", "🍾", "💋", "🖕", "😈", "😴", "😭", "🤓", "👻", "👨‍💻", "👀", "🎃", "🙈", "😇", "😨", "🤝", "✍", "🤗", "🫡", "🎅", "🎄", "☃", "💅", "🤪", "🗿", "🆒", "💘", "🙉", "🦄", "😘", "💊", "🙊", "😎", "👾", "🤷‍♂", "🤷", "🤷‍♀", "😡"
  */
-class ReactionTypeEmoji extends Entity implements ReactionType
+class ReactionTypeEmoji extends ReactionType
 {
-    public function __construct(array $data = [])
-    {
-        $data['type'] = 'emoji';
-        parent::__construct($data);
-    }
+    //
 }
diff --git a/src/Entities/ReactionType/ReactionTypeNotImplemented.php b/src/Entities/ReactionType/ReactionTypeNotImplemented.php
deleted file mode 100644
index 26fab61e5..000000000
--- a/src/Entities/ReactionType/ReactionTypeNotImplemented.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\ReactionType;
-
-use Longman\TelegramBot\Entities\Entity;
-
-class ReactionTypeNotImplemented extends Entity implements ReactionType
-{
-
-}
diff --git a/src/Entities/ReplyKeyboardMarkup.php b/src/Entities/ReplyKeyboardMarkup.php
new file mode 100644
index 000000000..7ede7dc7b
--- /dev/null
+++ b/src/Entities/ReplyKeyboardMarkup.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
+
+/**
+ * @method array[]     getKeyboard()              Array of button rows, each represented by an Array of KeyboardButton objects
+ * @method bool        isPersistent()             Optional. Requests clients to always show the keyboard when the regular keyboard is hidden. Defaults to false, in which case the custom keyboard can be hidden and opened with a keyboard icon.
+ * @method bool|null   getResizeKeyboard()        Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard.
+ * @method bool|null   getOneTimeKeyboard()       Optional. Requests clients to hide the keyboard as soon as it's been used. The keyboard will still be available, but clients will automatically display the usual letter-keyboard in the chat - the user can press a special button in the input field to see the custom keyboard again. Defaults to false.
+ * @method string|null getInputFieldPlaceholder() Optional. The placeholder to be shown in the input field when the keyboard is active; 1-64 characters
+ * @method bool|null   getSelective()             Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1), users that are @mentioned in the text of the Message object; 2), if the bot's message is a reply to a message in the same chat and forum topic, sender of the original message.
+ */
+class ReplyKeyboardMarkup extends Entity implements AllowsBypassingGet
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'keyboard' => [[KeyboardButton::class]],
+        ];
+    }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_persistent' => false,
+        ];
+    }
+}
diff --git a/src/Entities/ReplyKeyboardRemove.php b/src/Entities/ReplyKeyboardRemove.php
new file mode 100644
index 000000000..0980dcc3a
--- /dev/null
+++ b/src/Entities/ReplyKeyboardRemove.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method true      getRemoveKeyboard() Requests clients to remove the custom keyboard (user will not be able to summon this keyboard; if you want to hide the keyboard from sight but keep it accessible, use one_time_keyboard in ReplyKeyboardMarkup).
+ * @method bool|null getSelective()      Optional. Use this parameter if you want to remove the keyboard for specific users only. Targets: 1), users that are @mentioned in the text of the Message object; 2), if the bot's message is a reply to a message in the same chat and forum topic, sender of the original message.
+ */
+class ReplyKeyboardRemove extends Entity
+{
+    //
+}
diff --git a/src/Entities/ReplyParameters.php b/src/Entities/ReplyParameters.php
index 176650d9a..ab2eeb76d 100644
--- a/src/Entities/ReplyParameters.php
+++ b/src/Entities/ReplyParameters.php
@@ -1,35 +1,19 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\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
+ * @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|null      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). Not supported for messages sent on behalf of a business account.
+ * @method bool|null            getAllowSendingWithoutReply() Optional. Pass True if the message should be sent even if the specified message to be replied to is not found. Always False for replies in another chat or forum topic. Always True for messages sent on behalf of a business account.
+ * @method string|null          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|null          getQuoteParseMode()           Optional. Mode for parsing entities in the quote. See formatting options for more details.
+ * @method MessageEntity[]|null 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|null             getQuotePosition()            Optional. Position of the quote in the original message in UTF-16 code units
  */
 class ReplyParameters extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'quote_entities' => [MessageEntity::class],
diff --git a/src/Entities/ReplyToMessage.php b/src/Entities/ReplyToMessage.php
deleted file mode 100644
index 5dc914b0a..000000000
--- a/src/Entities/ReplyToMessage.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
-
-/**
- * Class ReplyToMessage
- *
- * @todo Is this even required?!
- */
-class ReplyToMessage extends Message
-{
-    /**
-     * ReplyToMessage constructor.
-     *
-     * @param array  $data
-     * @param string $bot_username
-     */
-    public function __construct(array $data, string $bot_username = '')
-    {
-        //As explained in the documentation
-        //Reply to message can't contain other reply to message entities
-        unset($data['reply_to_message']);
-
-        parent::__construct($data, $bot_username);
-    }
-}
diff --git a/src/Entities/ResponseParameters.php b/src/Entities/ResponseParameters.php
new file mode 100644
index 000000000..38dbd1c9c
--- /dev/null
+++ b/src/Entities/ResponseParameters.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int|null getMigrateToChatId() Optional. The group has been migrated to a supergroup with the specified identifier. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method int|null getRetryAfter()      Optional. In case of exceeding flood control, the number of seconds left to wait before the request can be repeated
+ */
+class ResponseParameters extends Entity
+{
+    //
+}
diff --git a/src/Entities/RevenueWithdrawalState/RevenueWithdrawalState.php b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalState.php
new file mode 100644
index 000000000..ff05e151e
--- /dev/null
+++ b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalState.php
@@ -0,0 +1,27 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\RevenueWithdrawalState;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the state
+ */
+abstract class RevenueWithdrawalState extends Entity implements Factory
+{
+    public const TYPE_PENDING = 'pending';
+
+    public const TYPE_SUCCEEDED = 'succeeded';
+
+    public const TYPE_FAILED = 'failed';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_PENDING   => new RevenueWithdrawalStatePending($data),
+            self::TYPE_SUCCEEDED => new RevenueWithdrawalStateSucceeded($data),
+            self::TYPE_FAILED    => new RevenueWithdrawalStateFailed($data),
+        };
+    }
+}
diff --git a/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStateFailed.php b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStateFailed.php
new file mode 100644
index 000000000..f19e40017
--- /dev/null
+++ b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStateFailed.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\RevenueWithdrawalState;
+
+class RevenueWithdrawalStateFailed extends RevenueWithdrawalState
+{
+    //
+}
diff --git a/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStatePending.php b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStatePending.php
new file mode 100644
index 000000000..c717ba79b
--- /dev/null
+++ b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStatePending.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\RevenueWithdrawalState;
+
+class RevenueWithdrawalStatePending extends RevenueWithdrawalState
+{
+    //
+}
diff --git a/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStateSucceeded.php b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStateSucceeded.php
new file mode 100644
index 000000000..a1b223c00
--- /dev/null
+++ b/src/Entities/RevenueWithdrawalState/RevenueWithdrawalStateSucceeded.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\RevenueWithdrawalState;
+
+/**
+ * @method int    getDate() Date the withdrawal was completed in Unix time
+ * @method string getUrl()  An HTTPS URL that can be used to see transaction details
+ */
+class RevenueWithdrawalStateSucceeded extends RevenueWithdrawalState
+{
+    //
+}
diff --git a/src/Entities/SentWebAppMessage.php b/src/Entities/SentWebAppMessage.php
index 27359b1a0..8c70f7c20 100644
--- a/src/Entities/SentWebAppMessage.php
+++ b/src/Entities/SentWebAppMessage.php
@@ -1,20 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class SentWebAppMessage
- * Contains information about an inline message sent by a Web App on behalf of a user.
- *
- * @link https://core.telegram.org/bots/api#sentwebappmessage
- *
- * @property string $inline_message_id Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.
- *
- * @method string getInlineMessageId() Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.
- *
- * @method $this setInlineMessageId(string $inline_message_id) Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.
+ * @method string|null getInlineMessageId() Optional. Identifier of the sent inline message. Available only if there is an inline keyboard attached to the message.
  */
 class SentWebAppMessage extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/ServerResponse.php b/src/Entities/ServerResponse.php
deleted file mode 100644
index fb18b2fbb..000000000
--- a/src/Entities/ServerResponse.php
+++ /dev/null
@@ -1,164 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- * 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\ChatMember\ChatMember;
-use Longman\TelegramBot\Entities\ChatMember\Factory as ChatMemberFactory;
-use Longman\TelegramBot\Entities\Games\GameHighScore;
-use Longman\TelegramBot\Entities\MenuButton\Factory as MenuButtonFactory;
-use Longman\TelegramBot\Request;
-
-/**
- * Class ServerResponse
- *
- * @link https://core.telegram.org/bots/api#making-requests
- *
- * @method bool   getOk()          If the request was successful
- * @method mixed  getResult()      The result of the query
- * @method int    getErrorCode()   Error code of the unsuccessful request
- * @method string getDescription() Human-readable description of the result / unsuccessful request
- *
- * @todo method ResponseParameters getParameters()  Field which can help to automatically handle the error
- */
-class ServerResponse extends Entity
-{
-    /**
-     * ServerResponse constructor.
-     *
-     * @param array  $data
-     * @param string $bot_username
-     */
-    public function __construct(array $data, string $bot_username = '')
-    {
-        $is_ok  = (bool) ($data['ok'] ?? false);
-        $result = $data['result'] ?? null;
-
-        if ($is_ok && is_array($result)) {
-            if ($this->isAssoc($result)) {
-                $data['result'] = $this->createResultObject($result, $bot_username);
-            } else {
-                $data['result'] = $this->createResultObjects($result, $bot_username);
-            }
-        }
-
-        parent::__construct($data, $bot_username);
-    }
-
-    /**
-     * Check if array is associative
-     *
-     * @link https://stackoverflow.com/a/4254008
-     *
-     * @param array $array
-     *
-     * @return bool
-     */
-    protected function isAssoc(array $array): bool
-    {
-        return count(array_filter(array_keys($array), 'is_string')) > 0;
-    }
-
-    /**
-     * If response is ok
-     *
-     * @return bool
-     */
-    public function isOk(): bool
-    {
-        return (bool) $this->getOk();
-    }
-
-    /**
-     * Print error
-     *
-     * @see https://secure.php.net/manual/en/function.print-r.php
-     *
-     * @param bool $return
-     *
-     * @return bool|string
-     */
-    public function printError($return = false)
-    {
-        $error = sprintf('Error N: %s, Description: %s', $this->getErrorCode(), $this->getDescription());
-
-        if ($return) {
-            return $error;
-        }
-
-        echo $error;
-
-        return true;
-    }
-
-    /**
-     * Create and return the object of the received result
-     *
-     * @param array  $result
-     * @param string $bot_username
-     *
-     * @return BotDescription|BotName|BotShortDescription|Chat|ChatAdministratorRights|ChatMember|File|Message|MenuButton|Poll|SentWebAppMessage|StickerSet|User|UserProfilePhotos|WebhookInfo
-     */
-    private function createResultObject(array $result, string $bot_username): Entity
-    {
-        $result_object_types = [
-            'getWebhookInfo'                  => WebhookInfo::class,
-            'getMe'                           => User::class,
-            'getUserProfilePhotos'            => UserProfilePhotos::class,
-            'getFile'                         => File::class,
-            'getChat'                         => Chat::class,
-            'getChatMember'                   => ChatMemberFactory::class,
-            'getMyName'                       => BotName::class,
-            'getMyDescription'                => BotDescription::class,
-            'getMyShortDescription'           => BotShortDescription::class,
-            'getChatMenuButton'               => MenuButtonFactory::class,
-            'getMyDefaultAdministratorRights' => ChatAdministratorRights::class,
-            'getStickerSet'                   => StickerSet::class,
-            'stopPoll'                        => Poll::class,
-            'answerWebAppQuery'               => SentWebAppMessage::class,
-        ];
-
-        $action       = Request::getCurrentAction();
-        $object_class = $result_object_types[$action] ?? Message::class;
-
-        return Factory::resolveEntityClass($object_class, $result, $bot_username);
-    }
-
-    /**
-     * Create and return the objects array of the received result
-     *
-     * @param array  $results
-     * @param string $bot_username
-     *
-     * @return BotCommand[]|ChatMember[]|GameHighScore[]|Message[]|Sticker[]|Update[]
-     */
-    private function createResultObjects(array $results, string $bot_username): array
-    {
-        $result_object_types = [
-            'getUpdates'                => Update::class,
-            'getChatAdministrators'     => ChatMemberFactory::class,
-            'getForumTopicIconStickers' => Sticker::class,
-            'getMyCommands'             => BotCommand::class,
-            'getCustomEmojiStickers'    => Sticker::class,
-            'getGameHighScores'         => GameHighScore::class,
-            'sendMediaGroup'            => Message::class,
-        ];
-
-        $action       = Request::getCurrentAction();
-        $object_class = $result_object_types[$action] ?? Update::class;
-
-        $objects = [];
-
-        foreach ($results as $result) {
-            $objects[] = Factory::resolveEntityClass($object_class, $result, $bot_username);
-        }
-
-        return $objects;
-    }
-}
diff --git a/src/Entities/SharedUser.php b/src/Entities/SharedUser.php
new file mode 100644
index 000000000..4ba8f6055
--- /dev/null
+++ b/src/Entities/SharedUser.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method int              getUserId()    Identifier of the shared user. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so 64-bit integers or double-precision float types are safe for storing these identifiers. The bot may not have access to the user and could be unable to use this identifier, unless the user is already known to the bot by some other means.
+ * @method string|null      getFirstName() Optional. First name of the user, if the name was requested by the bot
+ * @method string|null      getLastName()  Optional. Last name of the user, if the name was requested by the bot
+ * @method string|null      getUsername()  Optional. Username of the user, if the username was requested by the bot
+ * @method PhotoSize[]|null getPhoto()     Optional. Available sizes of the chat photo, if the photo was requested by the bot
+ */
+class SharedUser extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'photo' => [PhotoSize::class],
+        ];
+    }
+}
diff --git a/src/Entities/ShippingAddress.php b/src/Entities/ShippingAddress.php
new file mode 100644
index 000000000..05c69d92c
--- /dev/null
+++ b/src/Entities/ShippingAddress.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string getCountryCode() Two-letter ISO 3166-1 alpha-2 country code
+ * @method string getState()       State, if applicable
+ * @method string getCity()        City
+ * @method string getStreetLine1() First line for the address
+ * @method string getStreetLine2() Second line for the address
+ * @method string getPostCode()    Address post code
+ */
+class ShippingAddress extends Entity
+{
+    //
+}
diff --git a/src/Entities/ShippingOption.php b/src/Entities/ShippingOption.php
new file mode 100644
index 000000000..7b18326d5
--- /dev/null
+++ b/src/Entities/ShippingOption.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string         getId()     Shipping option identifier
+ * @method string         getTitle()  Option title
+ * @method LabeledPrice[] getPrices() List of price portions
+ */
+class ShippingOption extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'prices' => [LabeledPrice::class],
+        ];
+    }
+}
diff --git a/src/Entities/ShippingQuery.php b/src/Entities/ShippingQuery.php
new file mode 100644
index 000000000..85ee1c4af
--- /dev/null
+++ b/src/Entities/ShippingQuery.php
@@ -0,0 +1,20 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string          getId()              Unique query identifier
+ * @method User            getFrom()            User who sent the query
+ * @method string          getInvoicePayload()  Bot specified invoice payload
+ * @method ShippingAddress getShippingAddress() User specified shipping address
+ */
+class ShippingQuery extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'from'             => User::class,
+            'shipping_address' => ShippingAddress::class,
+        ];
+    }
+}
diff --git a/src/Entities/StarTransaction.php b/src/Entities/StarTransaction.php
new file mode 100644
index 000000000..d97c3e6b0
--- /dev/null
+++ b/src/Entities/StarTransaction.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+use PhpTelegramBot\Core\Entities\TransactionPartner\TransactionPartner;
+
+/**
+ * @method string                  getId()       Unique identifier of the transaction. Coincides with the identifer of the original transaction for refund transactions. Coincides with SuccessfulPayment.telegram_payment_charge_id for successful incoming payments from users.
+ * @method int                     getAmount()   Number of Telegram Stars transferred by the transaction
+ * @method int                     getDate()     Date the transaction was created in Unix time
+ * @method TransactionPartner|null getSource()   Optional. Source of an incoming transaction (e.g., a user purchasing goods or services, Fragment refunding a failed withdrawal). Only for incoming transactions
+ * @method TransactionPartner|null getReceiver() Optional. Receiver of an outgoing transaction (e.g., a user for a purchase refund, Fragment for a withdrawal). Only for outgoing transactions
+ */
+class StarTransaction extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'source'   => TransactionPartner::class,
+            'receiver' => TransactionPartner::class,
+        ];
+    }
+}
diff --git a/src/Entities/StarTransactions.php b/src/Entities/StarTransactions.php
new file mode 100644
index 000000000..bd2e4bf19
--- /dev/null
+++ b/src/Entities/StarTransactions.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+class StarTransactions extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'transactions' => [StarTransaction::class],
+        ];
+    }
+}
diff --git a/src/Entities/Sticker.php b/src/Entities/Sticker.php
index 3671c9523..69e727605 100644
--- a/src/Entities/Sticker.php
+++ b/src/Entities/Sticker.php
@@ -1,43 +1,33 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Sticker
- *
- * @link https://core.telegram.org/bots/api#sticker
- *
- * @method string       getFileId()           Identifier for this file, which can be used to download or reuse the file
- * @method string       getFileUniqueId()     Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method string       getType()             Type of the sticker, currently one of “regular”, “mask”, “custom_emoji”. The type of the sticker is independent from its format, which is determined by the fields is_animated and is_video.
- * @method int          getWidth()            Sticker width
- * @method int          getHeight()           Sticker height
- * @method bool         getIsAnimated()       True, if the sticker is animated
- * @method bool         getIsVideo()          True, if the sticker is a video sticker
- * @method PhotoSize    getThumbnail()        Optional. Sticker thumbnail in .webp or .jpg format
- * @method string       getEmoji()            Optional. Emoji associated with the sticker
- * @method string       getSetName()          Optional. Name of the sticker set to which the sticker belongs
- * @method File         getPremiumAnimation() Optional. Premium animation for the sticker, if the sticker is premium
- * @method MaskPosition getMaskPosition()     Optional. For mask stickers, the position where the mask should be placed
- * @method string       getCustomEmojiId()    Optional. For custom emoji stickers, unique identifier of the custom emoji
- * @method bool         getNeedsRepainting()  Optional. True, if the sticker must be repainted to a text color in messages, the color of the Telegram Premium badge in emoji status, white color on chat photos, or another appropriate color in other places
- * @method int          getFileSize()         Optional. File size
+ * @method string            getFileId()           Identifier for this file, which can be used to download or reuse the file
+ * @method string            getFileUniqueId()     Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method string            getType()             Type of the sticker, currently one of “regular”, “mask”, “custom_emoji”. The type of the sticker is independent from its format, which is determined by the fields is_animated and is_video.
+ * @method int               getWidth()            Sticker width
+ * @method int               getHeight()           Sticker height
+ * @method bool              getIsAnimated()       True, if the sticker is animated
+ * @method bool              getIsVideo()          True, if the sticker is a video sticker
+ * @method PhotoSize|null    getThumbnail()        Optional. Sticker thumbnail in the .WEBP or .JPG format
+ * @method string|null       getEmoji()            Optional. Emoji associated with the sticker
+ * @method string|null       getSetName()          Optional. Name of the sticker set to which the sticker belongs
+ * @method File|null         getPremiumAnimation() Optional. For premium regular stickers, premium animation for the sticker
+ * @method MaskPosition|null getMaskPosition()     Optional. For mask stickers, the position where the mask should be placed
+ * @method string|null       getCustomEmojiId()    Optional. For custom emoji stickers, unique identifier of the custom emoji
+ * @method true|null         getNeedsRepainting()  Optional. True, if the sticker must be repainted to a text color in messages, the color of the Telegram Premium badge in emoji status, white color on chat photos, or another appropriate color in other places
+ * @method int|null          getFileSize()         Optional. File size in bytes
  */
 class Sticker extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    public const TYPE_REGULAR = 'regular';
+
+    public const TYPE_MASK = 'mask';
+
+    public const TYPE_CUSTOM_EMOJI = 'custom_emoji';
+
+    protected static function subEntities(): array
     {
         return [
             'thumbnail'         => PhotoSize::class,
diff --git a/src/Entities/StickerSet.php b/src/Entities/StickerSet.php
index 8bb35ec4d..c76387e3b 100644
--- a/src/Entities/StickerSet.php
+++ b/src/Entities/StickerSet.php
@@ -1,35 +1,17 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class StickerSet
- *
- * @link https://core.telegram.org/bots/api#stickerset
- *
- * @method string    getName()          Sticker set name
- * @method string    getTitle()         Sticker set title
- * @method string    getStickerType()   Type of stickers in the set, currently one of “regular”, “mask”, “custom_emoji”
- * @method bool      getIsAnimated()    True, if the sticker set contains animated stickers
- * @method bool      getIsVideo()       True, if the sticker set contains video stickers
- * @method Sticker[] getStickers()      List of all set stickers
- * @method PhotoSize getThumbnail()     Optional. Sticker set thumbnail in the .WEBP or .TGS format
+ * @method string    getName()        Sticker set name
+ * @method string    getTitle()       Sticker set title
+ * @method string    getStickerType() Type of stickers in the set, currently one of “regular”, “mask”, “custom_emoji”
+ * @method Sticker[] getStickers()    List of all set stickers
+ * @method PhotoSize getThumbnail()   Optional. Sticker set thumbnail in the .WEBP, .TGS, or .WEBM format
  */
 class StickerSet extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'stickers'  => [Sticker::class],
diff --git a/src/Entities/Story.php b/src/Entities/Story.php
index 5647db224..263a3e28a 100644
--- a/src/Entities/Story.php
+++ b/src/Entities/Story.php
@@ -1,22 +1,17 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Story
- *
- * @link https://core.telegram.org/bots/api#story
+ * @method Chat getChat() Chat that posted the story
+ * @method int  getId()   Unique identifier for the story in the chat
  */
 class Story extends Entity
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'chat' => Chat::class,
+        ];
+    }
 }
diff --git a/src/Entities/SuccessfulPayment.php b/src/Entities/SuccessfulPayment.php
new file mode 100644
index 000000000..8a03b1d72
--- /dev/null
+++ b/src/Entities/SuccessfulPayment.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities;
+
+/**
+ * @method string         getCurrency()                Three-letter ISO 4217 currency code, or “XTR” for payments in Telegram Stars
+ * @method int            getTotalAmount()             Total price in the smallest units of the currency (integer, not float/double). For example, for a price of US$ 1.45 pass amount = 145. See the exp parameter in currencies.json, it shows the number of digits past the decimal point for each currency (2 for the majority of currencies).
+ * @method string         getInvoicePayload()          Bot specified invoice payload
+ * @method string|null    getShippingOptionId()        Optional. Identifier of the shipping option chosen by the user
+ * @method OrderInfo|null getOrderInfo()               Optional. Order information provided by the user
+ * @method string         getTelegramPaymentChargeId() Telegram payment identifier
+ * @method string         getProviderPaymentChargeId() Provider payment identifier
+ */
+class SuccessfulPayment extends Entity
+{
+    protected static function subEntities(): array
+    {
+        return [
+            'order_info' => OrderInfo::class,
+        ];
+    }
+}
diff --git a/src/Entities/SwitchInlineQueryChosenChat.php b/src/Entities/SwitchInlineQueryChosenChat.php
index 49218f6df..60d9fe106 100644
--- a/src/Entities/SwitchInlineQueryChosenChat.php
+++ b/src/Entities/SwitchInlineQueryChosenChat.php
@@ -1,21 +1,15 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class SwitchInlineQueryChosenChat
- *
- * This entity represents an inline button that switches the current user to inline mode in a chosen chat, with an optional default inline query.
- *
- * @link https://core.telegram.org/bots/api#switchinlinequerychosenchat
- *
- * @method string getQuery()             Optional. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted
- * @method bool   getAllowUserChats()    Optional. True, if private chats with users can be chosen
- * @method bool   getAllowBotChats()     Optional. True, if private chats with bots can be chosen
- * @method bool   getAllowGroupChats()   Optional. True, if group and supergroup chats can be chosen
- * @method bool   getAllowChannelChats() Optional. True, if channel chats can be chosen
+ * @method string|null getQuery()             Optional. The default inline query to be inserted in the input field. If left empty, only the bot's username will be inserted
+ * @method bool|null   getAllowUserChats()    Optional. True, if private chats with users can be chosen
+ * @method bool|null   getAllowBotChats()     Optional. True, if private chats with bots can be chosen
+ * @method bool|null   getAllowGroupChats()   Optional. True, if group and supergroup chats can be chosen
+ * @method bool|null   getAllowChannelChats() Optional. True, if channel chats can be chosen
  */
 class SwitchInlineQueryChosenChat extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/TelegramPassport/EncryptedCredentials.php b/src/Entities/TelegramPassport/EncryptedCredentials.php
deleted file mode 100644
index faa86d4bf..000000000
--- a/src/Entities/TelegramPassport/EncryptedCredentials.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\TelegramPassport;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class EncryptedCredentials
- *
- * Contains data required for decrypting and authenticating EncryptedCredentials. See the Telegram Passport Documentation for a complete description of the data decryption and authentication processes.
- *
- * @link https://core.telegram.org/bots/api#encryptedcredentials
- *
- * @method string getData()   Base64-encoded encrypted JSON-serialized data with unique user's payload, data hashes and secrets required for EncryptedPassportElement decryption and authentication
- * @method string getHash()   Base64-encoded data hash for data authentication
- * @method string getSecret() Base64-encoded secret, encrypted with the bot's public RSA key, required for data decryption
- **/
-class EncryptedCredentials extends Entity
-{
-
-}
diff --git a/src/Entities/TelegramPassport/EncryptedPassportElement.php b/src/Entities/TelegramPassport/EncryptedPassportElement.php
deleted file mode 100644
index 42a169cfd..000000000
--- a/src/Entities/TelegramPassport/EncryptedPassportElement.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\TelegramPassport;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class EncryptedPassportElement
- *
- * Contains information about documents or other Telegram Passport elements shared with the bot by the user.
- *
- * @link https://core.telegram.org/bots/api#encryptedpassportelement
- *
- * @method string         getType()        Element type. One of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”, “phone_number”, “email”.
- * @method string         getData()        Optional. Base64-encoded encrypted Telegram Passport element data provided by the user, available for “personal_details”, “passport”, “driver_license”, “identity_card”, “identity_passport” and “address” types. Can be decrypted and verified using the accompanying EncryptedCredentials.
- * @method string         getPhoneNumber() Optional. User's verified phone number, available only for “phone_number” type
- * @method string         getEmail()       Optional. User's verified email address, available only for “email” type
- * @method PassportFile[] getFiles()       Optional. Array of encrypted files with documents provided by the user, available for “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials.
- * @method PassportFile   getFrontSide()   Optional. Encrypted file with the front side of the document, provided by the user. Available for “passport”, “driver_license”, “identity_card” and “internal_passport”. The file can be decrypted and verified using the accompanying EncryptedCredentials.
- * @method PassportFile   getReverseSide() Optional. Encrypted file with the reverse side of the document, provided by the user. Available for “driver_license” and “identity_card”. The file can be decrypted and verified using the accompanying EncryptedCredentials.
- * @method PassportFile   getSelfie()      Optional. Encrypted file with the selfie of the user holding a document, provided by the user; available for “passport”, “driver_license”, “identity_card” and “internal_passport”. The file can be decrypted and verified using the accompanying EncryptedCredentials.
- * @method PassportFile[] getTranslation() Optional. Array of encrypted files with translated versions of documents provided by the user. Available if requested for “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration” and “temporary_registration” types. Files can be decrypted and verified using the accompanying EncryptedCredentials.
- * @method string         getHash()        Base64-encoded element hash for using in PassportElementErrorUnspecified
- **/
-class EncryptedPassportElement extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'files'        => [PassportFile::class],
-            'front_side'   => PassportFile::class,
-            'reverse_side' => PassportFile::class,
-            'selfie'       => PassportFile::class,
-            'translation'  => [PassportFile::class],
-        ];
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportData.php b/src/Entities/TelegramPassport/PassportData.php
deleted file mode 100644
index a845e1ed7..000000000
--- a/src/Entities/TelegramPassport/PassportData.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\TelegramPassport;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportData
- *
- * Contains information about Telegram Passport data shared with the bot by the user.
- *
- * @link https://core.telegram.org/bots/api#passportdata
- *
- * @method EncryptedPassportElement[] getData()        Array with information about documents and other Telegram Passport elements that was shared with the bot
- * @method EncryptedCredentials       getCredentials() Encrypted credentials required to decrypt the data
- **/
-class PassportData extends Entity
-{
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
-    {
-        return [
-            'data'        => [EncryptedPassportElement::class],
-            'credentials' => EncryptedCredentials::class,
-        ];
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementError.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementError.php
deleted file mode 100644
index e69e92de3..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementError.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-interface PassportElementError
-{
-
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorDataField.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorDataField.php
deleted file mode 100644
index d39881482..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorDataField.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorDataField
- *
- * Represents an issue in one of the data fields that was provided by the user. The error is considered resolved when the field's value changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrordatafield
- *
- * @method string getSource()    Error source, must be data
- * @method string getType()      The section of the user's Telegram Passport which has the error, one of “personal_details”, “passport”, “driver_license”, “identity_card”, “internal_passport”, “address”
- * @method string getFieldName() Name of the data field which has the error
- * @method string getDataHash()  Base64-encoded data hash
- * @method string getMessage()   Error message
- */
-class PassportElementErrorDataField extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorDataField constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'data';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFile.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFile.php
deleted file mode 100644
index eb980dd62..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFile.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorFile
- *
- * Represents an issue with a document scan. The error is considered resolved when the file with the document scan changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrorfile
- *
- * @method string getSource()   Error source, must be file
- * @method string getType()     The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
- * @method string getFileHash() Base64-encoded file hash
- * @method string getMessage()  Error message
- */
-class PassportElementErrorFile extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorFile constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'file';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFiles.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFiles.php
deleted file mode 100644
index 10ecc53ce..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFiles.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorFiles
- *
- * Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrorfiles
- *
- * @method string   getSource()     Error source, must be files
- * @method string   getType()       The section of the user's Telegram Passport which has the issue, one of “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
- * @method string[] getFileHashes() List of base64-encoded file hashes
- * @method string   getMessage()    Error message
- */
-class PassportElementErrorFiles extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorFiles constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'files';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFrontSide.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFrontSide.php
deleted file mode 100644
index a05ea4f25..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorFrontSide.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorFrontSide
- *
- * Represents an issue with the front side of a document. The error is considered resolved when the file with the front side of the document changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrorfrontside
- *
- * @method string getSource()   Error source, must be front_side
- * @method string getType()     The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”
- * @method string getFileHash() Base64-encoded hash of the file with the front side of the document
- * @method string getMessage()  Error message
- */
-class PassportElementErrorFrontSide extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorFrontSide constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'front_side';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorReverseSide.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorReverseSide.php
deleted file mode 100644
index e57d80ace..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorReverseSide.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorReverseSide
- *
- * Represents an issue with the reverse side of a document. The error is considered resolved when the file with reverse side of the document changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrorreverseside
- *
- * @method string getSource()   Error source, must be reverse_side
- * @method string getType()     The section of the user's Telegram Passport which has the issue, one of “driver_license”, “identity_card”
- * @method string getFileHash() Base64-encoded hash of the file with the reverse side of the document
- * @method string getMessage()  Error message
- */
-class PassportElementErrorReverseSide extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorReverseSide constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'reverse_side';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorSelfie.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorSelfie.php
deleted file mode 100644
index a7c9897bd..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorSelfie.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorSelfie
- *
- * Represents an issue with the selfie with a document. The error is considered resolved when the file with the selfie changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrorselfie
- *
- * @method string getSource()   Error source, must be selfie
- * @method string getType()     The section of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”
- * @method string getFileHash() Base64-encoded hash of the file with the selfie
- * @method string getMessage()  Error message
- */
-class PassportElementErrorSelfie extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorSelfie constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'selfie';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorTranslationFile.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorTranslationFile.php
deleted file mode 100644
index 267c342ce..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorTranslationFile.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorTranslationFile
- *
- * Represents an issue with one of the files that constitute the translation of a document. The error is considered resolved when the file changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrortranslationfile
- *
- * @method string getSource()   Error source, must be translation_file
- * @method string getType()     Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
- * @method string getFileHash() Base64-encoded translation_file hash
- * @method string getMessage()  Error message
- */
-class PassportElementErrorTranslationFile extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorTranslationFile constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'translation_file';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorTranslationFiles.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorTranslationFiles.php
deleted file mode 100644
index dd7b4de97..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorTranslationFiles.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorTranslationFiles
- *
- * Represents an issue with a list of scans. The error is considered resolved when the list of files containing the scans changes.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrortranslationfiles
- *
- * @method string   getSource()     Error source, must be translation_files
- * @method string   getType()       Type of element of the user's Telegram Passport which has the issue, one of “passport”, “driver_license”, “identity_card”, “internal_passport”, “utility_bill”, “bank_statement”, “rental_agreement”, “passport_registration”, “temporary_registration”
- * @method string[] getFileHashes() List of base64-encoded file hashes
- * @method string   getMessage()    Error message
- */
-class PassportElementErrorTranslationFiles extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorTranslationFiles constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'translation_files';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorUnspecified.php b/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorUnspecified.php
deleted file mode 100644
index c804dd2dd..000000000
--- a/src/Entities/TelegramPassport/PassportElementError/PassportElementErrorUnspecified.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Entities\TelegramPassport\PassportElementError;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportElementErrorUnspecified
- *
- * Represents an issue in an unspecified place. The error is considered resolved when new data is added.
- *
- * @link https://core.telegram.org/bots/api#passportelementerrorunspecified
- *
- * @method string getSource()      Error source, must be unspecified
- * @method string getType()        Type of element of the user's Telegram Passport which has the issue
- * @method string getElementHash() Base64-encoded element hash
- * @method string getMessage()     Error message
- */
-class PassportElementErrorUnspecified extends Entity implements PassportElementError
-{
-    /**
-     * PassportElementErrorUnspecified constructor
-     *
-     * @param array $data
-     */
-    public function __construct(array $data = [])
-    {
-        $data['source'] = 'unspecified';
-        parent::__construct($data);
-    }
-}
diff --git a/src/Entities/TelegramPassport/PassportFile.php b/src/Entities/TelegramPassport/PassportFile.php
deleted file mode 100644
index 13f6fa230..000000000
--- a/src/Entities/TelegramPassport/PassportFile.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities\TelegramPassport;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class PassportFile
- *
- * This object represents a file uploaded to Telegram Passport. Currently all Telegram Passport files are in JPEG format when decrypted and don't exceed 10MB.
- *
- * @link https://core.telegram.org/bots/api#passportfile
- *
- * @method string getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int    getFileSize()     File size
- * @method int    getFileDate()     Unix time when the file was uploaded
- **/
-class PassportFile extends Entity
-{
-
-}
diff --git a/src/Entities/TextQuote.php b/src/Entities/TextQuote.php
index 9e237d795..2136e6884 100644
--- a/src/Entities/TextQuote.php
+++ b/src/Entities/TextQuote.php
@@ -1,35 +1,28 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * 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.
+ * @method string               getText()     Text of the quoted part of a message that is replied to by the given message
+ * @method MessageEntity[]|null 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                 isManual()    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
+class TextQuote extends Entity implements AllowsBypassingGet
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'entities' => [MessageEntity::class],
         ];
     }
+
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_manual' => false,
+        ];
+    }
 }
diff --git a/src/Entities/Topics/ForumTopicClosed.php b/src/Entities/Topics/ForumTopicClosed.php
deleted file mode 100644
index 7adf25b66..000000000
--- a/src/Entities/Topics/ForumTopicClosed.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Topics;
-
-/**
- * Class ForumTopicClosed
- *
- * This object represents a service message about a forum topic closed in the chat. Currently holds no information.
- *
- * @link https://core.telegram.org/bots/api#forumtopicclosed
- */
-class ForumTopicClosed extends \Longman\TelegramBot\Entities\Entity
-{
-
-}
diff --git a/src/Entities/Topics/ForumTopicCreated.php b/src/Entities/Topics/ForumTopicCreated.php
deleted file mode 100644
index cf4326279..000000000
--- a/src/Entities/Topics/ForumTopicCreated.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Topics;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class ForumTopicCreated
- *
- * This object represents a service message about a new forum topic created in the chat.
- *
- * @link https://core.telegram.org/bots/api#forumtopiccreated
- *
- * @method string getName()              Name of the topic
- * @method int    getIconColor()         Color of the topic icon in RGB format
- * @method string getIconCustomEmojiId() Optional. Unique identifier of the custom emoji shown as the topic icon
- */
-class ForumTopicCreated extends Entity
-{
-
-}
diff --git a/src/Entities/Topics/ForumTopicEdited.php b/src/Entities/Topics/ForumTopicEdited.php
deleted file mode 100644
index aab0dc015..000000000
--- a/src/Entities/Topics/ForumTopicEdited.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Topics;
-
-use Longman\TelegramBot\Entities\Entity;
-
-/**
- * Class ForumTopicEdited
- *
- * This object represents a service message about an edited forum topic.
- *
- * @link https://core.telegram.org/bots/api#forumtopicedited
- *
- * @method string getName()              Optional. New name of the topic, if it was edited
- * @method string getIconCustomEmojiId() Optional. New identifier of the custom emoji shown as the topic icon, if it was edited; an empty string if the icon was removed
- */
-class ForumTopicEdited extends Entity
-{
-
-}
diff --git a/src/Entities/Topics/ForumTopicReopened.php b/src/Entities/Topics/ForumTopicReopened.php
deleted file mode 100644
index c30519360..000000000
--- a/src/Entities/Topics/ForumTopicReopened.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Topics;
-
-/**
- * Class ForumTopicReopened
- *
- * This object represents a service message about a forum topic reopened in the chat. Currently holds no information.
- *
- * @link https://core.telegram.org/bots/api#forumtopicreopened
- */
-class ForumTopicReopened extends \Longman\TelegramBot\Entities\Entity
-{
-
-}
diff --git a/src/Entities/Topics/GeneralForumTopicHidden.php b/src/Entities/Topics/GeneralForumTopicHidden.php
deleted file mode 100644
index fa5a8650d..000000000
--- a/src/Entities/Topics/GeneralForumTopicHidden.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Topics;
-
-/**
- * Class GeneralForumTopicHidden
- *
- * This object represents a service message about General forum topic hidden in the chat. Currently holds no information.
- *
- * @link https://core.telegram.org/bots/api#generalforumtopichidden
- */
-class GeneralForumTopicHidden extends \Longman\TelegramBot\Entities\Entity
-{
-
-}
diff --git a/src/Entities/Topics/GeneralForumTopicUnhidden.php b/src/Entities/Topics/GeneralForumTopicUnhidden.php
deleted file mode 100644
index d72dd8499..000000000
--- a/src/Entities/Topics/GeneralForumTopicUnhidden.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-namespace Longman\TelegramBot\Entities\Topics;
-
-/**
- * Class GeneralForumTopicUnhidden
- *
- * This object represents a service message about General forum topic unhidden in the chat. Currently holds no information.
- *
- * @link https://core.telegram.org/bots/api#generalforumtopicunhidden
- */
-class GeneralForumTopicUnhidden extends \Longman\TelegramBot\Entities\Entity
-{
-
-}
diff --git a/src/Entities/TransactionPartner/TransactionPartner.php b/src/Entities/TransactionPartner/TransactionPartner.php
new file mode 100644
index 000000000..92aa58250
--- /dev/null
+++ b/src/Entities/TransactionPartner/TransactionPartner.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\TransactionPartner;
+
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Entity;
+
+/**
+ * @method string getType() Type of the transaction partner
+ */
+abstract class TransactionPartner extends Entity implements Factory
+{
+    public const TYPE_USER = 'user';
+
+    public const TYPE_FRAGMENT = 'fragment';
+
+    public const TYPE_TELEGRAM_ADS = 'telegram_ads';
+
+    public const TYPE_OTHER = 'other';
+
+    public static function make(array $data): static
+    {
+        return match ($data['type']) {
+            self::TYPE_USER         => new TransactionPartnerUser($data),
+            self::TYPE_FRAGMENT     => new TransactionPartnerFragment($data),
+            self::TYPE_TELEGRAM_ADS => new TransactionPartnerTelegramAds($data),
+            self::TYPE_OTHER        => new TransactionPartnerOther($data),
+        };
+    }
+}
diff --git a/src/Entities/TransactionPartner/TransactionPartnerFragment.php b/src/Entities/TransactionPartner/TransactionPartnerFragment.php
new file mode 100644
index 000000000..c63b2694c
--- /dev/null
+++ b/src/Entities/TransactionPartner/TransactionPartnerFragment.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\TransactionPartner;
+
+use PhpTelegramBot\Core\Entities\RevenueWithdrawalState\RevenueWithdrawalState;
+
+/**
+ * @method RevenueWithdrawalState|null getWithdrawalState() Optional. State of the transaction if the transaction is outgoing
+ */
+class TransactionPartnerFragment extends TransactionPartner
+{
+    public static function subEntities(): array
+    {
+        return [
+            'withdrawal_state' => RevenueWithdrawalState::class,
+        ];
+    }
+}
diff --git a/src/Entities/TransactionPartner/TransactionPartnerOther.php b/src/Entities/TransactionPartner/TransactionPartnerOther.php
new file mode 100644
index 000000000..51e2a5ad6
--- /dev/null
+++ b/src/Entities/TransactionPartner/TransactionPartnerOther.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\TransactionPartner;
+
+class TransactionPartnerOther extends TransactionPartner
+{
+    //
+}
diff --git a/src/Entities/TransactionPartner/TransactionPartnerTelegramAds.php b/src/Entities/TransactionPartner/TransactionPartnerTelegramAds.php
new file mode 100644
index 000000000..ffbdf0091
--- /dev/null
+++ b/src/Entities/TransactionPartner/TransactionPartnerTelegramAds.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\TransactionPartner;
+
+class TransactionPartnerTelegramAds extends TransactionPartner
+{
+    //
+}
diff --git a/src/Entities/TransactionPartner/TransactionPartnerUser.php b/src/Entities/TransactionPartner/TransactionPartnerUser.php
new file mode 100644
index 000000000..cf51658d9
--- /dev/null
+++ b/src/Entities/TransactionPartner/TransactionPartnerUser.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace PhpTelegramBot\Core\Entities\TransactionPartner;
+
+use PhpTelegramBot\Core\Entities\User;
+
+/**
+ * @method User        getUser()           Information about the user
+ * @method string|null getInvoicePayload() Optional. Bot-specified invoice payload
+ */
+class TransactionPartnerUser extends TransactionPartner
+{
+    public static function subEntities(): array
+    {
+        return [
+            'user' => User::class,
+        ];
+    }
+}
diff --git a/src/Entities/Update.php b/src/Entities/Update.php
index 716b1830d..65b1b8e2e 100644
--- a/src/Entities/Update.php
+++ b/src/Entities/Update.php
@@ -1,132 +1,59 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * 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\Payments\PreCheckoutQuery;
-use Longman\TelegramBot\Entities\Payments\ShippingQuery;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Update
- *
- * @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 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.
+ * @method int                          getUpdateId()                The update's unique identifier. Update identifiers start from a certain positive number and increase sequentially. This identifier 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. If there are no new updates for at least a week, then identifier of the next update will be chosen randomly instead of sequentially.
+ * @method Message|null                 getMessage()                 Optional. New incoming message of any kind - text, photo, sticker, etc.
+ * @method Message|null                 getEditedMessage()           Optional. New version of a message that is known to the bot and was edited. This update may at times be triggered by changes to message fields that are either unavailable or not actively used by your bot.
+ * @method Message|null                 getChannelPost()             Optional. New incoming channel post of any kind - text, photo, sticker, etc.
+ * @method Message|null                 getEditedChannelPost()       Optional. New version of a channel post that is known to the bot and was edited. This update may at times be triggered by changes to message fields that are either unavailable or not actively used by your bot.
+ * @method BusinessConnection|null      getBusinessConnection()      Optional. The bot was connected to or disconnected from a business account, or a user edited an existing connection with the bot
+ * @method Message|null                 getBusinessMessage()         Optional. New message from a connected business account
+ * @method Message|null                 getEditedBusinessMessage()   Optional. New version of a message from a connected business account
+ * @method BusinessMessagesDeleted|null getDeletedBusinessMessages() Optional. Messages were deleted from a connected business account
+ * @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|null             getInlineQuery()             Optional. New incoming inline query
+ * @method ChosenInlineResult|null      getChosenInlineResult()      Optional. The result of an inline query that was chosen by a user and sent to their chat partner. Please see our documentation on the feedback collecting for details on how to enable these updates for your bot.
+ * @method CallbackQuery|null           getCallbackQuery()           Optional. New incoming callback query
+ * @method ShippingQuery|null           getShippingQuery()           Optional. New incoming shipping query. Only for invoices with flexible price
+ * @method PreCheckoutQuery|null        getPreCheckoutQuery()        Optional. New incoming pre-checkout query. Contains full information about checkout
+ * @method Poll|null                    getPoll()                    Optional. New poll state. Bots receive only updates about manually stopped polls and polls, which are sent by the bot
+ * @method PollAnswer|null              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|null       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|null       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|null         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|null        getChatBoost()               Optional. A chat boost was added or changed. The bot must be an administrator in the chat to receive these updates.
+ * @method ChatBoostRemoved|null        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_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}
-     */
-    protected function subEntities(): array
+    protected static 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_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,
+            'message'                   => Message::class,
+            'edited_message'            => Message::class,
+            'channel_post'              => Message::class,
+            'edited_channel_post'       => Message::class,
+            'business_connection'       => BusinessConnection::class,
+            'business_message'          => Message::class,
+            'edited_business_message'   => Message::class,
+            'deleted_business_messages' => BusinessMessagesDeleted::class,
+            'message_reaction'          => MessageReactionUpdated::class,
+            'message_reaction_count'    => MessageReactionCountUpdated::class,
+            'inline_query'              => InlineQuery::class,
+            'chosen_inline_result'      => ChosenInlineResult::class,
+            'callback_query'            => CallbackQuery::class,
+            'shipping_query'            => ShippingQuery::class,
+            'pre_checkout_query'        => PreCheckoutQuery::class,
+            'poll'                      => Poll::class,
+            'poll_answer'               => PollAnswer::class,
+            'my_chat_member'            => ChatMemberUpdated::class,
+            'chat_member'               => ChatMemberUpdated::class,
+            'chat_join_request'         => ChatJoinRequest::class,
+            'chat_boost'                => ChatBoostUpdated::class,
+            'removed_chat_boost'        => ChatBoostRemoved::class,
         ];
     }
-
-    /**
-     * Get the list of all available update types
-     *
-     * @return string[]
-     */
-    public static function getUpdateTypes(): array
-    {
-        return array_keys((new self([]))->subEntities());
-    }
-
-    /**
-     * Get the update type based on the set properties
-     *
-     * @return string|null
-     */
-    public function getUpdateType(): ?string
-    {
-        foreach (self::getUpdateTypes() as $type) {
-            if ($this->getProperty($type)) {
-                return $type;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Get update content
-     *
-     * @return Message|EditedMessage|ChannelPost|EditedChannelPost|MessageReactionUpdated|MessageReactionCountUpdated|InlineQuery|ChosenInlineResult|CallbackQuery|ShippingQuery|PreCheckoutQuery|Poll|PollAnswer|ChatMemberUpdated|ChatJoinRequest|ChatBoostUpdated|ChatBoostRemoved
-     */
-    public function getUpdateContent()
-    {
-        if ($update_type = $this->getUpdateType()) {
-            // Instead of just getting the property as an array,
-            // use the __call method to get the correct Entity object.
-            $method = 'get' . str_replace('_', '', ucwords($update_type, '_'));
-            return $this->$method();
-        }
-
-        return null;
-    }
 }
diff --git a/src/Entities/User.php b/src/Entities/User.php
index bc412b01f..f124796ba 100644
--- a/src/Entities/User.php
+++ b/src/Entities/User.php
@@ -1,34 +1,33 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * Class User
- *
- * @link https://core.telegram.org/bots/api#user
- *
- * @method int    getId()                      Unique identifier for this user or bot
- * @method bool   getIsBot()                   True, if this user is a bot
- * @method string getFirstName()               User's or bot’s first name
- * @method string getLastName()                Optional. User's or bot’s last name
- * @method string getUsername()                Optional. User's or bot’s username
- * @method string getLanguageCode()            Optional. IETF language tag of the user's language
- * @method bool   getIsPremium()               Optional. True, if this user is a Telegram Premium user
- * @method bool   getAddedToAttachmentMenu()   Optional. True, if this user added the bot to the attachment menu
- * @method bool   getCanJoinGroups()           Optional. True, if the bot can be invited to groups. Returned only in getMe.
- * @method bool   getCanReadAllGroupMessages() Optional. True, if privacy mode is disabled for the bot. Returned only in getMe.
- * @method bool   getSupportsInlineQueries()   Optional. True, if the bot supports inline queries. Returned only in getMe.
+ * @method int         getId()                    Unique identifier for this user or bot. This number may have more than 32 significant bits and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a 64-bit integer or double-precision float type are safe for storing this identifier.
+ * @method bool        isBot()                    True, if this user is a bot
+ * @method string      getFirstName()             User's or bot's first name
+ * @method string|null getLastName()              Optional. User's or bot's last name
+ * @method string|null getUsername()              Optional. User's or bot's username
+ * @method string|null getLanguageCode()          Optional. IETF language tag of the user's language
+ * @method bool        isPremium()                Optional. True, if this user is a Telegram Premium user
+ * @method true|null   getAddedToAttachmentMenu() Optional. True, if this user added the bot to the attachment menu
+ * @method bool        canJoinGroups()            Optional. True, if the bot can be invited to groups. Returned only in getMe.
+ * @method bool        canReadAllGroupMessages()  Optional. True, if privacy mode is disabled for the bot. Returned only in getMe.
+ * @method bool|null   getSupportsInlineQueries() Optional. True, if the bot supports inline queries. Returned only in getMe.
+ * @method bool        canConnectToBusiness()     Optional. True, if the bot can be connected to a Telegram Business account to receive its messages. Returned only in getMe.
  */
-class User extends Entity
+class User extends Entity implements AllowsBypassingGet
 {
-
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'is_bot'                      => false,
+            'is_premium'                  => false,
+            'can_join_groups'             => false,
+            'can_read_all_group_messages' => false,
+            'can_connect_to_business'     => false,
+        ];
+    }
 }
diff --git a/src/Entities/UserChatBoosts.php b/src/Entities/UserChatBoosts.php
index de45ddb14..32d013e25 100644
--- a/src/Entities/UserChatBoosts.php
+++ b/src/Entities/UserChatBoosts.php
@@ -1,29 +1,13 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\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
+    protected static function subEntities(): array
     {
         return [
             'boosts' => [ChatBoost::class],
diff --git a/src/Entities/UserProfilePhotos.php b/src/Entities/UserProfilePhotos.php
index e39dfc8eb..03d27987e 100644
--- a/src/Entities/UserProfilePhotos.php
+++ b/src/Entities/UserProfilePhotos.php
@@ -1,54 +1,17 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class UserProfilePhotos
- *
- * @link https://core.telegram.org/bots/api#userprofilephotos
- *
- * @method int getTotalCount() Total number of profile pictures the target user has
+ * @method int     getTotalCount() Total number of profile pictures the target user has
+ * @method array[] getPhotos()     Requested profile pictures (in up to 4 sizes each).
  */
 class UserProfilePhotos extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
-            'photos' => PhotoSize::class,
+            'photos' => [[PhotoSize::class]],
         ];
     }
-
-    /**
-     * Requested profile pictures (in up to 4 sizes each)
-     *
-     * This method overrides the default getPhotos method and returns a nice array
-     *
-     * @return PhotoSize[][]
-     */
-    public function getPhotos(): array
-    {
-        $all_photos = [];
-
-        if ($these_photos = $this->getProperty('photos')) {
-            foreach ($these_photos as $photos) {
-                $all_photos[] = array_map(function ($photo) {
-                    return new PhotoSize($photo);
-                }, $photos);
-            }
-        }
-
-        return $all_photos;
-    }
 }
diff --git a/src/Entities/UsersShared.php b/src/Entities/UsersShared.php
index ab32c2c90..571d41726 100644
--- a/src/Entities/UsersShared.php
+++ b/src/Entities/UsersShared.php
@@ -1,16 +1,17 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * This object contains information about the users whose identifiers were shared with the bot using a KeyboardButtonRequestUsers button.
- *
- * @link https://core.telegram.org/bots/api#usersshared
- *
- * @method int   getRequestId() Identifier of the request
- * @method int[] getUserIds()   Identifier of the shared users.
+ * @method int          getRequestId() Identifier of the request
+ * @method SharedUser[] getUsers()     Information about users shared with the bot.
  */
 class UsersShared extends Entity
 {
-
+    protected static function subEntities(): array
+    {
+        return [
+            'users' => [SharedUser::class],
+        ];
+    }
 }
diff --git a/src/Entities/Venue.php b/src/Entities/Venue.php
index 00925702d..d727ef8de 100644
--- a/src/Entities/Venue.php
+++ b/src/Entities/Venue.php
@@ -1,35 +1,19 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Venue
- *
- * @link https://core.telegram.org/bots/api#venue
- *
- * @method Location getLocation()        Venue location
- * @method string   getTitle()           Name of the venue
- * @method string   getAddress()         Address of the venue
- * @method string   getFoursquareId()    Optional. Foursquare identifier of the venue
- * @method string   getFoursquareType()  Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
- * @method string   getGooglePlaceId()   Optional. Google Places identifier of the venue
- * @method string   getGooglePlaceType() Optional. Google Places type of the venue
+ * @method Location    getLocation()                                                                                                                                                          Venue location. Can't be a live location
+ * @method string      getTitle()                                                                                                                                                             Name of the venue
+ * @method string      getAddress()                                                                                                                                                           Address of the venue
+ * @method string|null getFoursquareId()                                                                                                                                                      Optional. Foursquare identifier of the venue
+ * @method string|null getFoursquareType() Optional. Foursquare type of the venue. (For example, “arts_entertainment/default”, “arts_entertainment/aquarium” or “food/icecream”.)
+ * @method string|null getGooglePlaceId()                                                                                                                                                     Optional. Google Places identifier of the venue
+ * @method string|null getGooglePlaceType() Optional. Google Places type of the venue. (See supported types.)
  */
 class Venue extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'location' => Location::class,
diff --git a/src/Entities/Video.php b/src/Entities/Video.php
index 972f04da8..7a46e9878 100644
--- a/src/Entities/Video.php
+++ b/src/Entities/Video.php
@@ -1,37 +1,21 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Video
- *
- * @link https://core.telegram.org/bots/api#video
- *
- * @method string    getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string    getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int       getWidth()        Video width as defined by sender
- * @method int       getHeight()       Video height as defined by sender
- * @method int       getDuration()     Duration of the video in seconds as defined by sender
- * @method PhotoSize getThumbnail()    Optional. Video thumbnail
- * @method string    getFileName()     Optional. Original filename as defined by sender
- * @method string    getMimeType()     Optional. Mime type of a file as defined by sender
- * @method int       getFileSize()     Optional. File size
+ * @method string         getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string         getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int            getWidth()        Video width as defined by sender
+ * @method int            getHeight()       Video height as defined by sender
+ * @method int            getDuration()     Duration of the video in seconds as defined by sender
+ * @method PhotoSize|null getThumbnail()    Optional. Video thumbnail
+ * @method string|null    getFileName()     Optional. Original filename as defined by sender
+ * @method string|null    getMimeType()     Optional. MIME type of the file as defined by sender
+ * @method int|null       getFileSize()     Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.
  */
 class Video extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'thumbnail' => PhotoSize::class,
diff --git a/src/Entities/VideoChatEnded.php b/src/Entities/VideoChatEnded.php
index 5f3a52773..171c351ee 100644
--- a/src/Entities/VideoChatEnded.php
+++ b/src/Entities/VideoChatEnded.php
@@ -1,24 +1,11 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * This object represents a service message about a video chat ended in the chat.
- *
- * @link https://core.telegram.org/bots/api#videochatended
- *
- * @method int        getDuration()       Video chat duration in seconds
+ * @method int getDuration() Video chat duration in seconds
  */
 class VideoChatEnded extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/VideoChatParticipantsInvited.php b/src/Entities/VideoChatParticipantsInvited.php
index 36a67b58e..dca1cd6da 100644
--- a/src/Entities/VideoChatParticipantsInvited.php
+++ b/src/Entities/VideoChatParticipantsInvited.php
@@ -1,29 +1,13 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * This object represents a service message about new members invited to a video chat.
- *
- * @link https://core.telegram.org/bots/api#videochatparticipantsinvited
- *
- * @method User[]      getUsers()       New members that were invited to the video chat
+ * @method User[] getUsers() New members that were invited to the video chat
  */
 class VideoChatParticipantsInvited extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'users' => [User::class],
diff --git a/src/Entities/VideoChatScheduled.php b/src/Entities/VideoChatScheduled.php
index 6ac750197..652c7fcaf 100644
--- a/src/Entities/VideoChatScheduled.php
+++ b/src/Entities/VideoChatScheduled.php
@@ -1,24 +1,11 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * This object represents a service message about a video chat scheduled in the chat.
- *
- * @link https://core.telegram.org/bots/api#videochatscheduled
- *
  * @method int getStartDate() Point in time (Unix timestamp) when the video chat is supposed to be started by a chat administrator
  */
 class VideoChatScheduled extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/VideoChatStarted.php b/src/Entities/VideoChatStarted.php
index 4e10aa8d5..ee783f383 100644
--- a/src/Entities/VideoChatStarted.php
+++ b/src/Entities/VideoChatStarted.php
@@ -1,22 +1,8 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
-
-/**
- * This object represents a service message about a video chat started in the chat. Currently holds no information.
- *
- * @link https://core.telegram.org/bots/api#videochatstarted
- */
 class VideoChatStarted extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/VideoNote.php b/src/Entities/VideoNote.php
index 164934967..aa4e5685c 100644
--- a/src/Entities/VideoNote.php
+++ b/src/Entities/VideoNote.php
@@ -1,34 +1,18 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class VideoNote
- *
- * @link https://core.telegram.org/bots/api#videonote
- *
- * @method string    getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string    getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int       getLength()       Video width and height as defined by sender
- * @method int       getDuration()     Duration of the audio in seconds as defined by sender
- * @method PhotoSize getThumbnail()    Optional. Video thumbnail as defined by sender
- * @method int       getFileSize()     Optional. File size
+ * @method string         getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string         getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int            getLength()       Video width and height (diameter of the video message), as defined by sender
+ * @method int            getDuration()     Duration of the video in seconds as defined by sender
+ * @method PhotoSize|null getThumbnail()    Optional. Video thumbnail
+ * @method int|null       getFileSize()     Optional. File size in bytes
  */
 class VideoNote extends Entity
 {
-    /**
-     * {@inheritdoc}
-     */
-    protected function subEntities(): array
+    protected static function subEntities(): array
     {
         return [
             'thumbnail' => PhotoSize::class,
diff --git a/src/Entities/Voice.php b/src/Entities/Voice.php
index 1aab3b21e..ac6834e3c 100644
--- a/src/Entities/Voice.php
+++ b/src/Entities/Voice.php
@@ -1,28 +1,15 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class Voice
- *
- * @link https://core.telegram.org/bots/api#voice
- *
- * @method string getFileId()       Identifier for this file, which can be used to download or reuse the file
- * @method string getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
- * @method int    getDuration()     Duration of the audio in seconds as defined by sender
- * @method string getMimeType()     Optional. MIME type of the file as defined by sender
- * @method int    getFileSize()     Optional. File size
+ * @method string      getFileId()       Identifier for this file, which can be used to download or reuse the file
+ * @method string      getFileUniqueId() Unique identifier for this file, which is supposed to be the same over time and for different bots. Can't be used to download or reuse the file.
+ * @method int         getDuration()     Duration of the audio in seconds as defined by sender
+ * @method string|null getMimeType()     Optional. MIME type of the file as defined by sender
+ * @method int|null    getFileSize()     Optional. File size in bytes. It can be bigger than 2^31 and some programming languages may have difficulty/silent defects in interpreting it. But it has at most 52 significant bits, so a signed 64-bit integer or double-precision float type are safe for storing this value.
  */
-class Voice extends Entity
+class Voice
 {
-
+    //
 }
diff --git a/src/Entities/WebAppData.php b/src/Entities/WebAppData.php
index e23ec53c8..6dc3ca4f4 100644
--- a/src/Entities/WebAppData.php
+++ b/src/Entities/WebAppData.php
@@ -1,21 +1,12 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class WebAppData
- * Contains data sent from a Web App to the bot.
- *
- * @property string $data        The data. Be aware that a bad client can send arbitrary data in this field.
- * @property string $button_text Text of the web_app keyboard button, from which the Web App was opened. Be aware that a bad client can send arbitrary data in this field.
- *
  * @method string getData()       The data. Be aware that a bad client can send arbitrary data in this field.
- * @method string getButtonText() Text of the web_app keyboard button, from which the Web App was opened. Be aware that a bad client can send arbitrary data in this field.
- *
- * @method $this setData(string $data)              The data. Be aware that a bad client can send arbitrary data in this field.
- * @method $this setButtonText(string $button_text) Text of the web_app keyboard button, from which the Web App was opened. Be aware that a bad client can send arbitrary data in this field.
+ * @method string getButtonText() Text of the web_app keyboard button from which the Web App was opened. Be aware that a bad client can send arbitrary data in this field.
  */
 class WebAppData extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/WebAppInfo.php b/src/Entities/WebAppInfo.php
index 922774312..6af16abce 100644
--- a/src/Entities/WebAppInfo.php
+++ b/src/Entities/WebAppInfo.php
@@ -1,19 +1,11 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class WebAppInfo
- *
- * @link https://core.telegram.org/bots/api#webappinfo
- *
- * @property string $url An HTTPS URL of a Web App to be opened with additional data as specified in Initializing Web Apps
- *
  * @method string getUrl() An HTTPS URL of a Web App to be opened with additional data as specified in Initializing Web Apps
- *
- * @method $this setUrl(string $url) An HTTPS URL of a Web App to be opened with additional data as specified in Initializing Web Apps
  */
 class WebAppInfo extends Entity
 {
-
+    //
 }
diff --git a/src/Entities/WebhookInfo.php b/src/Entities/WebhookInfo.php
index abe30e4de..f4f733a1f 100644
--- a/src/Entities/WebhookInfo.php
+++ b/src/Entities/WebhookInfo.php
@@ -1,32 +1,27 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
+namespace PhpTelegramBot\Core\Entities;
 
-namespace Longman\TelegramBot\Entities;
+use PhpTelegramBot\Core\Contracts\AllowsBypassingGet;
 
 /**
- * Class WebhookInfo
- *
- * @link https://core.telegram.org/bots/api#webhookinfo
- *
- * @method string   getUrl()                          Webhook URL, may be empty if webhook is not set up
- * @method bool     getHasCustomCertificate()         True, if a custom certificate was provided for webhook certificate checks
- * @method int      getPendingUpdateCount()           Number of updates awaiting delivery
- * @method string   getIpAddress()                    Optional. Currently used webhook IP address
- * @method int      getLastErrorDate()                Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook
- * @method string   getLastErrorMessage()             Optional. Error message in human-readable format for the most recent error that happened when trying to deliver an update via webhook
- * @method int      getLastSynchronizationErrorDate() Optional. Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters
- * @method int      getMaxConnections()               Optional. Maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery
- * @method string[] getAllowedUpdates()               Optional. A list of update types the bot is subscribed to. Defaults to all update types
+ * @method string        getUrl()                          Webhook URL, may be empty if webhook is not set up
+ * @method bool          hasCustomCertificate()            True, if a custom certificate was provided for webhook certificate checks
+ * @method int           getPendingUpdateCount()           Number of updates awaiting delivery
+ * @method string|null   getIpAddress()                    Optional. Currently used webhook IP address
+ * @method int|null      getLastErrorDate()                Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook
+ * @method string|null   getLastErrorMessage()             Optional. Unix time for the most recent error that happened when trying to deliver an update via webhook
+ * @method int|null      getLastSynchronizationErrorDate() Optional. Unix time of the most recent error that happened when trying to synchronize available updates with Telegram datacenters
+ * @method int|null      getMaxConnections()               Optional. The maximum allowed number of simultaneous HTTPS connections to the webhook for update delivery
+ * @method string[]|null getAllowedUpdates()               Optional. A list of update types the bot is subscribed to. Defaults to all update types except chat_member
  */
-class WebhookInfo extends Entity
+class WebhookInfo extends Entity implements AllowsBypassingGet
 {
-
+    //
+    public static function fieldsBypassingGet(): array
+    {
+        return [
+            'has_custom_certificate' => false,
+        ];
+    }
 }
diff --git a/src/Entities/WriteAccessAllowed.php b/src/Entities/WriteAccessAllowed.php
index 5785732eb..c0d9dca90 100644
--- a/src/Entities/WriteAccessAllowed.php
+++ b/src/Entities/WriteAccessAllowed.php
@@ -1,19 +1,13 @@
 <?php
 
-namespace Longman\TelegramBot\Entities;
+namespace PhpTelegramBot\Core\Entities;
 
 /**
- * Class WriteAccessAllowed
- *
- * This object represents a service message about a user allowing a bot added to the attachment menu to write messages. Currently holds no information.
- *
- * @link https://core.telegram.org/bots/api#writeaccessallowed
- *
- * @method bool   getFromRequest()        Optional. True, if the access was granted after the user accepted an explicit request from a Web App sent by the method requestWriteAccess
- * @method string getWebAppName()         Optional. Name of the Web App, if the access was granted when the Web App was launched from a link
- * @method bool   getFromAttachmentMenu() Optional. True, if the access was granted when the bot was added to the attachment or side menu
+ * @method bool|null   getFromRequest()        Optional. True, if the access was granted after the user accepted an explicit request from a Web App sent by the method requestWriteAccess
+ * @method string|null getWebAppName()         Optional. Name of the Web App, if the access was granted when the Web App was launched from a link
+ * @method bool|null   getFromAttachmentMenu() Optional. True, if the access was granted when the bot was added to the attachment or side menu
  */
 class WriteAccessAllowed extends Entity
 {
-
+    //
 }
diff --git a/src/Events/Event.php b/src/Events/Event.php
new file mode 100644
index 000000000..df825e371
--- /dev/null
+++ b/src/Events/Event.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpTelegramBot\Core\Events;
+
+abstract class Event
+{
+}
diff --git a/src/Events/IncomingUpdate.php b/src/Events/IncomingUpdate.php
new file mode 100644
index 000000000..6f54adca3
--- /dev/null
+++ b/src/Events/IncomingUpdate.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Events;
+
+use PhpTelegramBot\Core\Entities\Update;
+
+class IncomingUpdate extends Event
+{
+    public function __construct(
+        public Update $update,
+    ) {
+    }
+}
diff --git a/src/Events/UnregisteredCommand.php b/src/Events/UnregisteredCommand.php
new file mode 100644
index 000000000..832e21c08
--- /dev/null
+++ b/src/Events/UnregisteredCommand.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Events;
+
+use PhpTelegramBot\Core\Entities\Update;
+
+class UnregisteredCommand extends Event
+{
+    public function __construct(
+        public Update $update,
+    ) {
+    }
+}
diff --git a/src/Events/UnregisteredMessageType.php b/src/Events/UnregisteredMessageType.php
new file mode 100644
index 000000000..c68770de9
--- /dev/null
+++ b/src/Events/UnregisteredMessageType.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Events;
+
+use PhpTelegramBot\Core\Entities\Update;
+
+class UnregisteredMessageType extends Event
+{
+    public function __construct(
+        public Update $update,
+    ) {
+    }
+}
diff --git a/src/Events/UnregisteredUpdateType.php b/src/Events/UnregisteredUpdateType.php
new file mode 100644
index 000000000..cd001dee5
--- /dev/null
+++ b/src/Events/UnregisteredUpdateType.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace PhpTelegramBot\Core\Events;
+
+use PhpTelegramBot\Core\Entities\Update;
+
+class UnregisteredUpdateType extends Event
+{
+    public function __construct(
+        public Update $update,
+    ) {
+    }
+}
diff --git a/src/Exception/InvalidBotTokenException.php b/src/Exception/InvalidBotTokenException.php
deleted file mode 100644
index 6184b2a0f..000000000
--- a/src/Exception/InvalidBotTokenException.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Exception;
-
-/**
- * Thrown when bot token is invalid
- */
-class InvalidBotTokenException extends TelegramException
-{
-    /**
-     * InvalidBotTokenException constructor
-     */
-    public function __construct()
-    {
-        parent::__construct('Invalid bot token!');
-    }
-}
diff --git a/src/Exception/TelegramException.php b/src/Exception/TelegramException.php
deleted file mode 100644
index fb0acbec1..000000000
--- a/src/Exception/TelegramException.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Exception;
-
-use Exception;
-
-/**
- * Main exception class used for exception handling
- */
-class TelegramException extends Exception
-{
-
-}
diff --git a/src/Exception/TelegramLogException.php b/src/Exception/TelegramLogException.php
deleted file mode 100644
index 282ab5557..000000000
--- a/src/Exception/TelegramLogException.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Exception;
-
-/**
- * Main exception class used for exception handling
- */
-class TelegramLogException extends TelegramException
-{
-
-}
diff --git a/src/Exceptions/InvalidArgumentException.php b/src/Exceptions/InvalidArgumentException.php
new file mode 100644
index 000000000..b3217772e
--- /dev/null
+++ b/src/Exceptions/InvalidArgumentException.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace PhpTelegramBot\Core\Exceptions;
+
+class InvalidArgumentException extends \InvalidArgumentException
+{
+    //
+}
diff --git a/src/Exceptions/TelegramException.php b/src/Exceptions/TelegramException.php
new file mode 100644
index 000000000..dab58db61
--- /dev/null
+++ b/src/Exceptions/TelegramException.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace PhpTelegramBot\Core\Exceptions;
+
+class TelegramException extends \Exception
+{
+}
diff --git a/src/HttpClient.php b/src/HttpClient.php
new file mode 100644
index 000000000..78acda05b
--- /dev/null
+++ b/src/HttpClient.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace PhpTelegramBot\Core;
+
+use Http\Discovery\Psr17FactoryDiscovery;
+use Http\Discovery\Psr18ClientDiscovery;
+use Http\Message\MultipartStream\MultipartStreamBuilder;
+use Psr\Http\Client\ClientInterface;
+use Psr\Http\Message\RequestFactoryInterface;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\StreamFactoryInterface;
+use Psr\Http\Message\StreamInterface;
+
+class HttpClient
+{
+    public function __construct(
+        protected ?ClientInterface $client = null,
+        protected ?RequestFactoryInterface $requestFactory = null,
+        protected ?StreamFactoryInterface $streamFactory = null,
+    ) {
+        $this->client ??= Psr18ClientDiscovery::find();
+        $this->requestFactory ??= Psr17FactoryDiscovery::findRequestFactory();
+        $this->streamFactory ??= Psr17FactoryDiscovery::findStreamFactory();
+    }
+
+    public function get(string $uri): ResponseInterface
+    {
+        return $this->client->sendRequest(
+            $this->requestFactory->createRequest('GET', $uri)
+        );
+    }
+
+    public function postJson(string $uri, array $data): ResponseInterface
+    {
+        $json = json_encode($data);
+
+        return $this->client->sendRequest(
+            $this->requestFactory->createRequest('POST', $uri)
+                ->withHeader('Content-Type', 'application/json')
+                ->withBody(
+                    $this->streamFactory->createStream($json)
+                )
+        );
+    }
+
+    /**
+     * @param array<StreamInterface> $streams
+     *
+     * @throws \Psr\Http\Client\ClientExceptionInterface
+     */
+    public function postMultipart(string $uri, array $data, array $streams): ResponseInterface
+    {
+        $builder = new MultipartStreamBuilder($this->streamFactory);
+
+        // Add data
+        foreach ($data as $key => $value) {
+            $value = match (true) {
+                is_array($value)    => json_encode($value),
+                default             => (string) $value,
+            };
+
+            $builder->addResource($key, $value);
+        }
+
+        // Add file streams
+        foreach ($streams as $fileId => $stream) {
+            $builder->addResource($fileId, $stream);
+        }
+
+        $boundary = $builder->getBoundary();
+
+        return $this->client->sendRequest(
+            $this->requestFactory->createRequest('POST', $uri)
+                ->withHeader('Content-Type', "multipart/form-data; boundary=\"$boundary\"")
+                ->withBody($builder->build())
+        );
+    }
+
+    public function streamFactory(): StreamFactoryInterface
+    {
+        return $this->streamFactory;
+    }
+}
diff --git a/src/Request.php b/src/Request.php
deleted file mode 100644
index 21a7a5f7a..000000000
--- a/src/Request.php
+++ /dev/null
@@ -1,1038 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot;
-
-use GuzzleHttp\Client;
-use GuzzleHttp\ClientInterface;
-use GuzzleHttp\Exception\ConnectException;
-use GuzzleHttp\Exception\RequestException;
-use GuzzleHttp\Psr7\Stream;
-use Longman\TelegramBot\Entities\File;
-use Longman\TelegramBot\Entities\InputMedia\InputMedia;
-use Longman\TelegramBot\Entities\Message;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Exception\InvalidBotTokenException;
-use Longman\TelegramBot\Exception\TelegramException;
-use Throwable;
-
-/**
- * Class Request
- *
- * @method static ServerResponse getUpdates(array $data)                      Use this method to receive incoming updates using long polling (wiki). An Array of Update objects is returned.
- * @method static ServerResponse setWebhook(array $data)                      Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. In case of an unsuccessful request, we will give up after a reasonable amount of attempts. Returns true.
- * @method static ServerResponse deleteWebhook(array $data)                   Use this method to remove webhook integration if you decide to switch back to getUpdates. Returns True on success.
- * @method static ServerResponse getWebhookInfo()                             Use this method to get current webhook status. Requires no parameters. On success, returns a WebhookInfo object. If the bot is using getUpdates, will return an object with the url field empty.
- * @method static ServerResponse getMe()                                      A simple method for testing your bot's auth token. Requires no parameters. Returns basic information about the bot in form of a User object.
- * @method static ServerResponse logOut()                                     Use this method to log out from the cloud Bot API server before launching the bot locally. Requires no parameters. Returns True on success.
- * @method static ServerResponse close()                                      Use this method to close the bot instance before moving it from one local server to another. Requires no parameters. Returns True on success.
- * @method static ServerResponse forwardMessage(array $data)                  Use this method to forward messages of any kind. On success, the sent Message is returned.
- * @method static ServerResponse forwardMessages(array $data)                 Use this method to forward multiple messages of any kind. If some of the specified messages can't be found or forwarded, they are skipped. Service messages and messages with protected content can't be forwarded. Album grouping is kept for forwarded messages. On success, an array of MessageId of the sent messages is returned.
- * @method static ServerResponse copyMessage(array $data)                     Use this method to copy messages of any kind. The method is analogous to the method forwardMessages, but the copied message doesn't have a link to the original message. Returns the MessageId of the sent message on success.
- * @method static ServerResponse copyMessages(array $data)                    Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. A quiz poll can be copied only if the value of the field correct_option_id is known to the bot. The method is analogous to the method forwardMessages, but the copied messages don't have a link to the original message. Album grouping is kept for copied messages. On success, an array of MessageId of the sent messages is returned.
- * @method static ServerResponse sendPhoto(array $data)                       Use this method to send photos. On success, the sent Message is returned.
- * @method static ServerResponse sendAudio(array $data)                       Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. On success, the sent Message is returned. Bots can currently send audio files of up to 50 MB in size, this limit may be changed in the future.
- * @method static ServerResponse sendDocument(array $data)                    Use this method to send general files. On success, the sent Message is returned. Bots can currently send files of any type of up to 50 MB in size, this limit may be changed in the future.
- * @method static ServerResponse sendSticker(array $data)                     Use this method to send .webp stickers. On success, the sent Message is returned.
- * @method static ServerResponse sendVideo(array $data)                       Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). On success, the sent Message is returned. Bots can currently send video files of up to 50 MB in size, this limit may be changed in the future.
- * @method static ServerResponse sendAnimation(array $data)                   Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). On success, the sent Message is returned. Bots can currently send animation files of up to 50 MB in size, this limit may be changed in the future.
- * @method static ServerResponse sendVoice(array $data)                       Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as Audio or Document). On success, the sent Message is returned. Bots can currently send voice messages of up to 50 MB in size, this limit may be changed in the future.
- * @method static ServerResponse sendVideoNote(array $data)                   Use this method to send video messages. On success, the sent Message is returned.
- * @method static ServerResponse sendMediaGroup(array $data)                  Use this method to send a group of photos or videos as an album. On success, an array of the sent Messages is returned.
- * @method static ServerResponse sendLocation(array $data)                    Use this method to send point on the map. On success, the sent Message is returned.
- * @method static ServerResponse editMessageLiveLocation(array $data)         Use this method to edit live location messages sent by the bot or via the bot (for inline bots). A location can be edited until its live_period expires or editing is explicitly disabled by a call to stopMessageLiveLocation. On success, if the edited message was sent by the bot, the edited Message is returned, otherwise True is returned.
- * @method static ServerResponse stopMessageLiveLocation(array $data)         Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires. On success, if the message was sent by the bot, the sent Message is returned, otherwise True is returned.
- * @method static ServerResponse sendVenue(array $data)                       Use this method to send information about a venue. On success, the sent Message is returned.
- * @method static ServerResponse sendContact(array $data)                     Use this method to send phone contacts. On success, the sent Message is returned.
- * @method static ServerResponse sendPoll(array $data)                        Use this method to send a native poll. A native poll can't be sent to a private chat. On success, the sent Message is returned.
- * @method static ServerResponse sendDice(array $data)                        Use this method to send a dice, which will have a random value from 1 to 6. On success, the sent Message is returned.
- * @method static ServerResponse sendChatAction(array $data)                  Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). Returns True on success.
- * @method static ServerResponse setMessageReaction(array $data)              Use this method to change the chosen reactions on a message. Service messages can't be reacted to. Automatically forwarded messages from a channel to its discussion group have the same available reactions as messages in the channel. Returns True on success.
- * @method static ServerResponse getUserProfilePhotos(array $data)            Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos object.
- * @method static ServerResponse getFile(array $data)                         Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. On success, a File object is returned. The file can then be downloaded via the link https://api.telegram.org/file/bot<token>/<file_path>, where <file_path> 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.
- * @method static ServerResponse unbanChatMember(array $data)                 Use this method to unban a previously kicked user in a supergroup or channel. The user will not return to the group or channel automatically, but will be able to join via link, etc. The bot must be an administrator for this to work. Returns True on success.
- * @method static ServerResponse restrictChatMember(array $data)              Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all permissions to lift restrictions from a user. Returns True on success.
- * @method static ServerResponse promoteChatMember(array $data)               Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Pass False for all boolean parameters to demote a user. Returns True on success.
- * @method static ServerResponse setChatAdministratorCustomTitle(array $data) Use this method to set a custom title for an administrator in a supergroup promoted by the bot. Returns True on success.
- * @method static ServerResponse banChatSenderChat(array $data)               Use this method to ban a channel chat in a supergroup or a channel. Until the chat is unbanned, the owner of the banned chat won't be able to send messages on behalf of any of their channels. The bot must be an administrator in the supergroup or channel for this to work and must have the appropriate administrator rights. Returns True on success.
- * @method static ServerResponse unbanChatSenderChat(array $data)             Use this method to unban a previously banned channel chat in a supergroup or channel. The bot must be an administrator for this to work and must have the appropriate administrator rights. Returns True on success.
- * @method static ServerResponse setChatPermissions(array $data)              Use this method to set default chat permissions for all members. The bot must be an administrator in the group or a supergroup for this to work and must have the can_restrict_members admin rights. Returns True on success.
- * @method static ServerResponse exportChatInviteLink(array $data)            Use this method to generate a new invite link for a chat. Any previously generated link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the new invite link as String on success.
- * @method static ServerResponse createChatInviteLink(array $data)            Use this method to create an additional invite link for a chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. The link can be revoked using the method revokeChatInviteLink. Returns the new invite link as ChatInviteLink object.
- * @method static ServerResponse editChatInviteLink(array $data)              Use this method to edit a non-primary invite link created by the bot. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the edited invite link as a ChatInviteLink object.
- * @method static ServerResponse revokeChatInviteLink(array $data)            Use this method to revoke an invite link created by the bot. If the primary link is revoked, a new link is automatically generated. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns the revoked invite link as ChatInviteLink object.
- * @method static ServerResponse approveChatJoinRequest(array $data)          Use this method to approve a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success.
- * @method static ServerResponse declineChatJoinRequest(array $data)          Use this method to decline a chat join request. The bot must be an administrator in the chat for this to work and must have the can_invite_users administrator right. Returns True on success.
- * @method static ServerResponse setChatPhoto(array $data)                    Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
- * @method static ServerResponse deleteChatPhoto(array $data)                 Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
- * @method static ServerResponse setChatTitle(array $data)                    Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
- * @method static ServerResponse setChatDescription(array $data)              Use this method to change the description of a group, a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Returns True on success.
- * @method static ServerResponse pinChatMessage(array $data)                  Use this method to pin a message in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the ‘can_pin_messages’ admin right in the supergroup or ‘can_edit_messages’ admin right in the channel. Returns True on success.
- * @method static ServerResponse unpinChatMessage(array $data)                Use this method to unpin a message in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the ‘can_pin_messages’ admin right in the supergroup or ‘can_edit_messages’ admin right in the channel. Returns True on success.
- * @method static ServerResponse unpinAllChatMessages(array $data)            Use this method to clear the list of pinned messages in a chat. If the chat is not a private chat, the bot must be an administrator in the chat for this to work and must have the 'can_pin_messages' admin right in a supergroup or 'can_edit_messages' admin right in a channel. Returns True on success.
- * @method static ServerResponse leaveChat(array $data)                       Use this method for your bot to leave a group, supergroup or channel. Returns True on success.
- * @method static ServerResponse getChat(array $data)                         Use this method to get up to date information about the chat (current name of the user for one-on-one conversations, current username of a user, group or channel, etc.). Returns a Chat object on success.
- * @method static ServerResponse getChatAdministrators(array $data)           Use this method to get a list of administrators in a chat. On success, returns an Array of ChatMember objects that contains information about all chat administrators except other bots. If the chat is a group or a supergroup and no administrators were appointed, only the creator will be returned.
- * @method static ServerResponse getChatMemberCount(array $data)              Use this method to get the number of members in a chat. Returns Int on success.
- * @method static ServerResponse getChatMember(array $data)                   Use this method to get information about a member of a chat. Returns a ChatMember object on success.
- * @method static ServerResponse setChatStickerSet(array $data)               Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success.
- * @method static ServerResponse deleteChatStickerSet(array $data)            Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. Use the field can_set_sticker_set optionally returned in getChat requests to check if the bot can use this method. Returns True on success.
- * @method static ServerResponse getForumTopicIconStickers(array $data)       Use this method to get custom emoji stickers, which can be used as a forum topic icon by any user. Requires no parameters. Returns an Array of Sticker objects
- * @method static ServerResponse createForumTopic(array $data)                Use this method to create a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns information about the created topic as a ForumTopic object.
- * @method static ServerResponse editForumTopic(array $data)                  Use this method to edit name and icon of a topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights, unless it is the creator of the topic. Returns True on success.
- * @method static ServerResponse closeForumTopic(array $data)                 Use this method to close an open topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. Returns True on success.
- * @method static ServerResponse reopenForumTopic(array $data)                Use this method to reopen a closed topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights, unless it is the creator of the topic. Returns True on success.
- * @method static ServerResponse deleteForumTopic(array $data)                Use this method to delete a forum topic along with all its messages in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_delete_messages administrator rights. Returns True on success.
- * @method static ServerResponse unpinAllForumTopicMessages(array $data)      Use this method to clear the list of pinned messages in a 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 editGeneralForumTopic(array $data)           Use this method to edit the name of the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have can_manage_topics administrator rights. Returns True on success.
- * @method static ServerResponse closeGeneralForumTopic(array $data)          Use this method to close an open 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns True on success.
- * @method static ServerResponse reopenGeneralForumTopic(array $data)         Use this method to reopen a closed 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. The topic will be automatically unhidden if it was hidden. Returns True on success.
- * @method static ServerResponse hideGeneralForumTopic(array $data)           Use this method to hide the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. The topic will be automatically closed if it was open. Returns True on success.
- * @method static ServerResponse unhideGeneralForumTopic(array $data)         Use this method to unhide the 'General' topic in a forum supergroup chat. The bot must be an administrator in the chat for this to work and must have the can_manage_topics administrator rights. Returns True on success.
- * @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.
- * @method static ServerResponse setMyName(array $data)                       Use this method to change the bot's name. Returns True on success.
- * @method static ServerResponse getMyName(array $data)                       Use this method to get the current bot name for the given user language. Returns BotName on success.
- * @method static ServerResponse setMyDescription(array $data)                Use this method to change the bot's description, which is shown in the chat with the bot if the chat is empty. Returns True on success.
- * @method static ServerResponse getMyDescription(array $data)                Use this method to get the current bot description for the given user language. Returns BotDescription on success.
- * @method static ServerResponse setMyShortDescription(array $data)           Use this method to change the bot's short description, which is shown on the bot's profile page and is sent together with the link when users share the bot. Returns True on success.
- * @method static ServerResponse getMyShortDescription(array $data)           Use this method to get the current bot short description for the given user language. Returns BotShortDescription on success.
- * @method static ServerResponse setChatMenuButton(array $data)               Use this method to change the bot's menu button in a private chat, or the default menu button. Returns True on success.
- * @method static ServerResponse getChatMenuButton(array $data)               Use this method to get the current value of the bot's menu button in a private chat, or the default menu button. Returns MenuButton on success.
- * @method static ServerResponse setMyDefaultAdministratorRights(array $data) Use this method to change the default administrator rights requested by the bot when it's added as an administrator to groups or channels. These rights will be suggested to users, but they are are free to modify the list before adding the bot. Returns True on success.
- * @method static ServerResponse getMyDefaultAdministratorRights(array $data) Use this method to get the current default administrator rights of the bot. Returns ChatAdministratorRights on success.
- * @method static ServerResponse editMessageText(array $data)                 Use this method to edit text and game 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 editMessageCaption(array $data)              Use this method to edit captions 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 editMessageMedia(array $data)                Use this method to edit audio, document, photo, or video messages. On success, if the edited message was sent by the bot, the edited Message is returned, otherwise True is returned.
- * @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.
- * @method static ServerResponse createNewStickerSet(array $data)             Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. Returns True on success.
- * @method static ServerResponse addStickerToSet(array $data)                 Use this method to add a new sticker to a set created by the bot. Returns True on success.
- * @method static ServerResponse setStickerPositionInSet(array $data)         Use this method to move a sticker in a set created by the bot to a specific position. Returns True on success.
- * @method static ServerResponse deleteStickerFromSet(array $data)            Use this method to delete a sticker from a set created by the bot. Returns True on success.
- * @method static ServerResponse setStickerEmojiList(array $data)             Use this method to change the list of emoji assigned to a regular or custom emoji sticker. The sticker must belong to a sticker set created by the bot. Returns True on success.
- * @method static ServerResponse setStickerKeywords(array $data)              Use this method to change search keywords assigned to a regular or custom emoji sticker. The sticker must belong to a sticker set created by the bot. Returns True on success.
- * @method static ServerResponse setStickerMaskPosition(array $data)          Use this method to change the mask position of a mask sticker. The sticker must belong to a sticker set that was created by the bot. Returns True on success.
- * @method static ServerResponse setStickerSetTitle(array $data)              Use this method to set the title of a created sticker set. Returns True on success.
- * @method static ServerResponse setStickerSetThumbnail(array $data)          Use this method to set the thumbnail of a sticker set. Animated thumbnails can be set for animated sticker sets only. Returns True on success.
- * @method static ServerResponse setCustomEmojiStickerSetThumbnail(array $data) Use this method to set the thumbnail of a custom emoji sticker set. Returns True on success.
- * @method static ServerResponse deleteStickerSet(array $data)                Use this method to delete a sticker set that was created by the bot. Returns True on success.
- * @method static ServerResponse answerWebAppQuery(array $data)               Use this method to set the result of an interaction with a Web App and send a corresponding message on behalf of the user to the chat from which the query originated. On success, a SentWebAppMessage object is returned.
- * @method static ServerResponse sendInvoice(array $data)                     Use this method to send invoices. On success, the sent Message is returned.
- * @method static ServerResponse createInvoiceLink(array $data)               Use this method to create a link for an invoice. Returns the created invoice link as String on success.
- * @method static ServerResponse answerShippingQuery(array $data)             If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. Use this method to reply to shipping queries. On success, True is returned.
- * @method static ServerResponse answerPreCheckoutQuery(array $data)          Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. Use this method to respond to such pre-checkout queries. On success, True is returned.
- * @method static ServerResponse setPassportDataErrors(array $data)           Informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed (the contents of the field for which you returned the error must change). Returns True on success. Use this if the data submitted by the user doesn't satisfy the standards your service requires for any reason. For example, if a birthday date seems invalid, a submitted document is blurry, a scan shows evidence of tampering, etc. Supply some details in the error message to make sure the user knows how to correct the issues.
- * @method static ServerResponse sendGame(array $data)                        Use this method to send a game. On success, the sent Message is returned.
- * @method static ServerResponse setGameScore(array $data)                    Use this method to set the score of the specified user in a game. On success, if the message was sent by the bot, returns the edited Message, otherwise returns True. Returns an error, if the new score is not greater than the user's current score in the chat and force is False.
- * @method static ServerResponse getGameHighScores(array $data)               Use this method to get data for high score tables. Will return the score of the specified user and several of his neighbors in a game. On success, returns an Array of GameHighScore objects.
- */
-class Request
-{
-    /**
-     * Telegram object
-     *
-     * @var Telegram
-     */
-    private static $telegram;
-
-    /**
-     * URI of the Telegram API
-     *
-     * @var string
-     */
-    private static $api_base_uri = 'https://api.telegram.org';
-
-    /**
-     * URI of the Telegram API for downloading files (relative to $api_base_url or absolute)
-     *
-     * @var string
-     */
-    private static $api_base_download_uri = '/file/bot{API_KEY}';
-
-    /**
-     * Guzzle Client object
-     *
-     * @var ClientInterface
-     */
-    private static $client;
-
-    /**
-     * Request limiter
-     *
-     * @var bool
-     */
-    private static $limiter_enabled;
-
-    /**
-     * Request limiter's interval between checks
-     *
-     * @var float
-     */
-    private static $limiter_interval;
-
-    /**
-     * The current action that is being executed
-     *
-     * @var string
-     */
-    private static $current_action = '';
-
-    /**
-     * Available actions to send
-     *
-     * This is basically the list of all methods listed on the official API documentation.
-     *
-     * @link https://core.telegram.org/bots/api
-     *
-     * @var array
-     */
-    private static $actions = [
-        'getUpdates',
-        'setWebhook',
-        'deleteWebhook',
-        'getWebhookInfo',
-        'getMe',
-        'logOut',
-        'close',
-        'sendMessage',
-        'forwardMessage',
-        'forwardMessages',
-        'copyMessage',
-        'copyMessages',
-        'sendPhoto',
-        'sendAudio',
-        'sendDocument',
-        'sendSticker',
-        'sendVideo',
-        'sendAnimation',
-        'sendVoice',
-        'sendVideoNote',
-        'sendMediaGroup',
-        'sendLocation',
-        'editMessageLiveLocation',
-        'stopMessageLiveLocation',
-        'sendVenue',
-        'sendContact',
-        'sendPoll',
-        'sendDice',
-        'sendChatAction',
-        'setMessageReaction',
-        'getUserProfilePhotos',
-        'getFile',
-        'banChatMember',
-        'unbanChatMember',
-        'restrictChatMember',
-        'promoteChatMember',
-        'setChatAdministratorCustomTitle',
-        'banChatSenderChat',
-        'unbanChatSenderChat',
-        'setChatPermissions',
-        'exportChatInviteLink',
-        'createChatInviteLink',
-        'editChatInviteLink',
-        'revokeChatInviteLink',
-        'approveChatJoinRequest',
-        'declineChatJoinRequest',
-        'setChatPhoto',
-        'deleteChatPhoto',
-        'setChatTitle',
-        'setChatDescription',
-        'pinChatMessage',
-        'unpinChatMessage',
-        'unpinAllChatMessages',
-        'leaveChat',
-        'getChat',
-        'getChatAdministrators',
-        'getChatMemberCount',
-        'getChatMember',
-        'setChatStickerSet',
-        'deleteChatStickerSet',
-        'getForumTopicIconStickers',
-        'createForumTopic',
-        'editForumTopic',
-        'closeForumTopic',
-        'reopenForumTopic',
-        'deleteForumTopic',
-        'unpinAllForumTopicMessages',
-        'editGeneralForumTopic',
-        'closeGeneralForumTopic',
-        'reopenGeneralForumTopic',
-        'hideGeneralForumTopic',
-        'unhideGeneralForumTopic',
-        'unpinAllGeneralForumTopicMessages',
-        'answerCallbackQuery',
-        'answerInlineQuery',
-        'getUserChatBoosts',
-        'setMyCommands',
-        'deleteMyCommands',
-        'getMyCommands',
-        'setMyName',
-        'getMyName',
-        'setMyDescription',
-        'getMyDescription',
-        'setMyShortDescription',
-        'getMyShortDescription',
-        'setChatMenuButton',
-        'getChatMenuButton',
-        'setMyDefaultAdministratorRights',
-        'getMyDefaultAdministratorRights',
-        'editMessageText',
-        'editMessageCaption',
-        'editMessageMedia',
-        'editMessageReplyMarkup',
-        'stopPoll',
-        'deleteMessage',
-        'deleteMessages',
-        'getStickerSet',
-        'getCustomEmojiStickers',
-        'uploadStickerFile',
-        'createNewStickerSet',
-        'addStickerToSet',
-        'setStickerPositionInSet',
-        'deleteStickerFromSet',
-        'setStickerEmojiList',
-        'setStickerKeywords',
-        'setStickerMaskPosition',
-        'setStickerSetTitle',
-        'setStickerSetThumbnail',
-        'setCustomEmojiStickerSetThumbnail',
-        'deleteStickerSet',
-        'answerWebAppQuery',
-        'sendInvoice',
-        'createInvoiceLink',
-        'answerShippingQuery',
-        'answerPreCheckoutQuery',
-        'setPassportDataErrors',
-        'sendGame',
-        'setGameScore',
-        'getGameHighScores',
-    ];
-
-    /**
-     * Methods that don't require any data need a dummy param due to certain cURL issues.
-     *
-     * @see Request::addDummyParamIfNecessary()
-     *
-     * @var array
-     */
-    private static $actions_need_dummy_param = [
-        'deleteWebhook',
-        'getWebhookInfo',
-        'getMe',
-        'logOut',
-        'close',
-        'deleteMyCommands',
-        'getMyCommands',
-        'setMyName',
-        'getMyName',
-        'setMyDescription',
-        'getMyDescription',
-        'setMyShortDescription',
-        'getMyShortDescription',
-        'setChatMenuButton',
-        'getChatMenuButton',
-        'setMyDefaultAdministratorRights',
-        'getMyDefaultAdministratorRights',
-    ];
-
-    /**
-     * Available fields for InputFile helper
-     *
-     * This is basically the list of all fields that allow InputFile objects
-     * for which input can be simplified by providing local path directly as string.
-     *
-     * @var array
-     */
-    private static $input_file_fields = [
-        'setWebhook'          => ['certificate'],
-        'sendPhoto'           => ['photo'],
-        'sendAudio'           => ['audio', 'thumbnail'],
-        'sendDocument'        => ['document', 'thumbnail'],
-        'sendVideo'           => ['video', 'thumbnail'],
-        'sendAnimation'       => ['animation', 'thumbnail'],
-        'sendVoice'           => ['voice'],
-        'sendVideoNote'       => ['video_note', 'thumbnail'],
-        'setChatPhoto'        => ['photo'],
-        'sendSticker'         => ['sticker'],
-        'uploadStickerFile'   => ['sticker'],
-        // @todo Look into new InputSticker field and see if we can do the same there.
-        // 'createNewStickerSet' => ['png_sticker', 'tgs_sticker', 'webm_sticker'],
-        // 'addStickerToSet'     => ['png_sticker', 'tgs_sticker', 'webm_sticker'],
-        'setStickerSetThumbnail' => ['thumbnail'],
-    ];
-
-    /**
-     * Initialize
-     *
-     * @param Telegram $telegram
-     */
-    public static function initialize(Telegram $telegram): void
-    {
-        self::$telegram = $telegram;
-        self::setClient(self::$client ?: new Client(['base_uri' => self::$api_base_uri]));
-    }
-
-    /**
-     * Set a custom Guzzle HTTP Client object
-     *
-     * @param ClientInterface $client
-     */
-    public static function setClient(ClientInterface $client): void
-    {
-        self::$client = $client;
-    }
-
-    /**
-     * Set a custom Bot API URL
-     *
-     * @param string $api_base_uri
-     * @param string $api_base_download_uri
-     */
-    public static function setCustomBotApiUri(string $api_base_uri, string $api_base_download_uri = ''): void
-    {
-        self::$api_base_uri = $api_base_uri;
-        if ($api_base_download_uri !== '') {
-            self::$api_base_download_uri = $api_base_download_uri;
-        }
-    }
-
-    /**
-     * Get input from custom input or stdin and return it
-     *
-     * @return string
-     */
-    public static function getInput(): string
-    {
-        // First check if a custom input has been set, else get the PHP input.
-        return self::$telegram->getCustomInput()
-            ?: file_get_contents('php://input');
-    }
-
-    /**
-     * Generate general fake server response
-     *
-     * @param array $data Data to add to fake response
-     *
-     * @return array Fake response data
-     */
-    public static function generateGeneralFakeServerResponse(array $data = []): array
-    {
-        //PARAM BINDED IN PHPUNIT TEST FOR TestServerResponse.php
-        //Maybe this is not the best possible implementation
-
-        //No value set in $data ie testing setWebhook
-        //Provided $data['chat_id'] ie testing sendMessage
-
-        $fake_response = ['ok' => true]; // :)
-
-        if ($data === []) {
-            $fake_response['result'] = true;
-        }
-
-        //some data to initialize the class method SendMessage
-        if (isset($data['chat_id'])) {
-            $data['message_id'] = '1234';
-            $data['date']       = '1441378360';
-            $data['from']       = [
-                'id'         => 123456789,
-                'first_name' => 'botname',
-                'username'   => 'namebot',
-            ];
-            $data['chat']       = ['id' => $data['chat_id']];
-
-            $fake_response['result'] = $data;
-        }
-
-        return $fake_response;
-    }
-
-    /**
-     * Properly set up the request params
-     *
-     * If any item of the array is a resource, reformat it to a multipart request.
-     * Else, just return the passed data as form params.
-     *
-     * @param array $data
-     *
-     * @return array
-     * @throws TelegramException
-     */
-    private static function setUpRequestParams(array $data): array
-    {
-        $has_resource = false;
-        $multipart    = [];
-
-        foreach ($data as $key => &$item) {
-            if ($key === 'media') {
-                // Magical media input helper.
-                $item = self::mediaInputHelper($item, $has_resource, $multipart);
-            } elseif (array_key_exists(self::$current_action, self::$input_file_fields) && in_array($key, self::$input_file_fields[self::$current_action], true)) {
-                // Allow absolute paths to local files.
-                if (is_string($item) && file_exists($item)) {
-                    $item = new Stream(self::encodeFile($item));
-                }
-            } elseif (is_array($item) || is_object($item)) {
-                // Convert any nested arrays or objects into JSON strings.
-                $item = json_encode($item);
-            }
-
-            // Reformat data array in multipart way if it contains a resource
-            $has_resource = $has_resource || is_resource($item) || $item instanceof Stream;
-            $multipart[]  = ['name' => $key, 'contents' => $item];
-        }
-        unset($item);
-
-        if ($has_resource) {
-            return ['multipart' => $multipart];
-        }
-
-        return ['form_params' => $data];
-    }
-
-    /**
-     * Magical input media helper to simplify passing media.
-     *
-     * This allows the following:
-     * Request::editMessageMedia([
-     *     ...
-     *     'media' => new InputMediaPhoto([
-     *         'caption' => 'Caption!',
-     *         'media'   => Request::encodeFile($local_photo),
-     *     ]),
-     * ]);
-     * and
-     * Request::sendMediaGroup([
-     *     'media'   => [
-     *         new InputMediaPhoto(['media' => Request::encodeFile($local_photo_1)]),
-     *         new InputMediaPhoto(['media' => Request::encodeFile($local_photo_2)]),
-     *         new InputMediaVideo(['media' => Request::encodeFile($local_video_1)]),
-     *     ],
-     * ]);
-     * and even
-     * Request::sendMediaGroup([
-     *     'media'   => [
-     *         new InputMediaPhoto(['media' => $local_photo_1]),
-     *         new InputMediaPhoto(['media' => $local_photo_2]),
-     *         new InputMediaVideo(['media' => $local_video_1]),
-     *     ],
-     * ]);
-     *
-     * @param mixed $item
-     * @param bool  $has_resource
-     * @param array $multipart
-     *
-     * @return mixed
-     * @throws TelegramException
-     */
-    private static function mediaInputHelper($item, bool &$has_resource, array &$multipart)
-    {
-        $was_array = is_array($item);
-        $was_array || $item = [$item];
-
-        /** @var InputMedia|null $media_item */
-        foreach ($item as $media_item) {
-            if (!($media_item instanceof InputMedia)) {
-                continue;
-            }
-
-            // Make a list of all possible media that can be handled by the helper.
-            $possible_medias = array_filter([
-                'media'     => $media_item->getMedia(),
-                'thumbnail' => $media_item->getThumbnail(),
-            ]);
-
-            foreach ($possible_medias as $type => $media) {
-                // Allow absolute paths to local files.
-                if (is_string($media) && strpos($media, 'attach://') !== 0 && file_exists($media)) {
-                    $media = new Stream(self::encodeFile($media));
-                }
-
-                if (is_resource($media) || $media instanceof Stream) {
-                    $has_resource = true;
-                    $unique_key   = uniqid($type . '_', false);
-                    $multipart[]  = ['name' => $unique_key, 'contents' => $media];
-
-                    // We're literally overwriting the passed media type data!
-                    $media_item->$type           = 'attach://' . $unique_key;
-                    $media_item->raw_data[$type] = 'attach://' . $unique_key;
-                }
-            }
-        }
-
-        $was_array || $item = reset($item);
-
-        return json_encode($item);
-    }
-
-    /**
-     * Get the current action that's being executed
-     *
-     * @return string
-     */
-    public static function getCurrentAction(): string
-    {
-        return self::$current_action;
-    }
-
-    /**
-     * Execute HTTP Request
-     *
-     * @param string $action Action to execute
-     * @param array  $data   Data to attach to the execution
-     *
-     * @return string Result of the HTTP Request
-     * @throws TelegramException
-     */
-    public static function execute(string $action, array $data = []): string
-    {
-        $request_params          = self::setUpRequestParams($data);
-        $request_params['debug'] = TelegramLog::getDebugLogTempStream();
-
-        try {
-            $response = self::$client->post(
-                '/bot' . self::$telegram->getApiKey() . '/' . $action,
-                $request_params
-            );
-            $result   = (string) $response->getBody();
-        } catch (ConnectException $e) {
-            $response = null;
-            $result   = $e->getMessage();
-        } catch (RequestException $e) {
-            $response = null;
-            $result   = $e->getResponse() ? (string) $e->getResponse()->getBody() : '';
-        }
-
-        //Logging verbose debug output
-        if (TelegramLog::$always_log_request_and_response || $response === null) {
-            TelegramLog::debug('Request data:' . PHP_EOL . print_r($data, true));
-            TelegramLog::debug('Response data:' . PHP_EOL . $result);
-            TelegramLog::endDebugLogTempStream('Verbose HTTP Request output:' . PHP_EOL . '%s' . PHP_EOL);
-        }
-
-        return $result;
-    }
-
-    /**
-     * Download file
-     *
-     * @param File $file
-     *
-     * @return bool
-     * @throws TelegramException
-     */
-    public static function downloadFile(File $file): bool
-    {
-        if (empty($download_path = self::$telegram->getDownloadPath())) {
-            throw new TelegramException('Download path not set!');
-        }
-
-        $tg_file_path = $file->getFilePath();
-        $file_path    = $download_path . '/' . $tg_file_path;
-
-        $file_dir = dirname($file_path);
-        //For safety reasons, first try to create the directory, then check that it exists.
-        //This is in case some other process has created the folder in the meantime.
-        if (!@mkdir($file_dir, 0755, true) && !is_dir($file_dir)) {
-            throw new TelegramException('Directory ' . $file_dir . ' can\'t be created');
-        }
-
-        $debug_handle = TelegramLog::getDebugLogTempStream();
-
-        try {
-            $base_download_uri = str_replace('{API_KEY}', self::$telegram->getApiKey(), self::$api_base_download_uri);
-            self::$client->get(
-                "{$base_download_uri}/{$tg_file_path}",
-                ['debug' => $debug_handle, 'sink' => $file_path]
-            );
-
-            return filesize($file_path) > 0;
-        } catch (Throwable $e) {
-            return false;
-        } finally {
-            //Logging verbose debug output
-            TelegramLog::endDebugLogTempStream('Verbose HTTP File Download Request output:' . PHP_EOL . '%s' . PHP_EOL);
-        }
-    }
-
-    /**
-     * Encode file
-     *
-     * @param string $file
-     *
-     * @return resource
-     * @throws TelegramException
-     */
-    public static function encodeFile(string $file)
-    {
-        $fp = fopen($file, 'rb');
-        if ($fp === false) {
-            throw new TelegramException('Cannot open "' . $file . '" for reading');
-        }
-
-        return $fp;
-    }
-
-    /**
-     * Send command
-     *
-     * @todo Fake response doesn't need json encoding?
-     * @todo Write debug entry on failure
-     *
-     * @param string $action
-     * @param array  $data
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public static function send(string $action, array $data = []): ServerResponse
-    {
-        self::ensureValidAction($action);
-        self::addDummyParamIfNecessary($action, $data);
-
-        $bot_username = self::$telegram->getBotUsername();
-
-        if (defined('PHPUNIT_TESTSUITE')) {
-            $fake_response = self::generateGeneralFakeServerResponse($data);
-
-            return new ServerResponse($fake_response, $bot_username);
-        }
-
-        self::ensureNonEmptyData($data);
-
-        self::limitTelegramRequests($action, $data);
-
-        // Remember which action is currently being executed.
-        self::$current_action = $action;
-
-        $raw_response = self::execute($action, $data);
-        $response     = json_decode($raw_response, true);
-
-        if (null === $response) {
-            TelegramLog::debug($raw_response);
-            throw new TelegramException('Telegram returned an invalid response!');
-        }
-
-        $response = new ServerResponse($response, $bot_username);
-
-        if (!$response->isOk() && $response->getErrorCode() === 401 && $response->getDescription() === 'Unauthorized') {
-            throw new InvalidBotTokenException();
-        }
-
-        // Special case for sent polls, which need to be saved specially.
-        // @todo Take into account if DB gets extracted into separate module.
-        if ($response->isOk() && ($message = $response->getResult()) && ($message instanceof Message) && $poll = $message->getPoll()) {
-            DB::insertPollRequest($poll);
-        }
-
-        // Reset current action after completion.
-        self::$current_action = '';
-
-        return $response;
-    }
-
-    /**
-     * Add a dummy parameter if the passed action requires it.
-     *
-     * If a method doesn't require parameters, we need to add a dummy one anyway,
-     * because of some cURL version failed POST request without parameters.
-     *
-     * @link https://github.com/php-telegram-bot/core/pull/228
-     *
-     * @todo Would be nice to find a better solution for this!
-     *
-     * @param string $action
-     * @param array  $data
-     */
-    protected static function addDummyParamIfNecessary(string $action, array &$data): void
-    {
-        if (empty($data) && in_array($action, self::$actions_need_dummy_param, true)) {
-            // Can be anything, using a single letter to minimise request size.
-            $data = ['d'];
-        }
-    }
-
-    /**
-     * Make sure the data isn't empty, else throw an exception
-     *
-     * @param array $data
-     *
-     * @throws TelegramException
-     */
-    private static function ensureNonEmptyData(array $data): void
-    {
-        if (count($data) === 0) {
-            throw new TelegramException('Data is empty!');
-        }
-    }
-
-    /**
-     * Make sure the action is valid, else throw an exception
-     *
-     * @param string $action
-     *
-     * @throws TelegramException
-     */
-    private static function ensureValidAction(string $action): void
-    {
-        if (!in_array($action, self::$actions, true)) {
-            throw new TelegramException('The action "' . $action . '" doesn\'t exist!');
-        }
-    }
-
-    /**
-     * Use this method to send text messages. On success, the last sent Message is returned
-     *
-     * All message responses are saved in `$extras['responses']`.
-     * Custom encoding can be defined in `$extras['encoding']` (default: `mb_internal_encoding()`)
-     * Custom splitting can be defined in `$extras['split']` (default: 4096)
-     *     `$extras['split'] = null;` // force to not split message at all!
-     *     `$extras['split'] = 200;`  // split message into 200 character chunks
-     *
-     * @link https://core.telegram.org/bots/api#sendmessage
-     *
-     * @todo Splitting formatted text may break the message.
-     *
-     * @param array      $data
-     * @param array|null $extras
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public static function sendMessage(array $data, ?array &$extras = []): ServerResponse
-    {
-        $extras = array_merge([
-            'split'    => 4096,
-            'encoding' => mb_internal_encoding(),
-        ], (array) $extras);
-
-        $text       = $data['text'];
-        $encoding   = $extras['encoding'];
-        $max_length = $extras['split'] ?: mb_strlen($text, $encoding);
-
-        $responses = [];
-
-        do {
-            // Chop off and send the first message.
-            $data['text'] = mb_substr($text, 0, $max_length, $encoding);
-            $responses[]  = self::send('sendMessage', $data);
-
-            // Prepare the next message.
-            $text = mb_substr($text, $max_length, null, $encoding);
-        } while ($text !== '');
-
-        // Add all response objects to referenced variable.
-        $extras['responses'] = $responses;
-
-        return end($responses);
-    }
-
-    /**
-     * Any statically called method should be relayed to the `send` method.
-     *
-     * @param string $action
-     * @param array  $data
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public static function __callStatic(string $action, array $data): ServerResponse
-    {
-        // Only argument should be the data array, ignore any others.
-        return static::send($action, reset($data) ?: []);
-    }
-
-    /**
-     * Return an empty Server Response
-     *
-     * No request is sent to Telegram.
-     * This function is used in commands that don't need to fire a message after execution
-     *
-     * @return ServerResponse
-     */
-    public static function emptyResponse(): ServerResponse
-    {
-        return new ServerResponse(['ok' => true, 'result' => true]);
-    }
-
-    /**
-     * Send message to all active chats
-     *
-     * @param string $callback_function
-     * @param array  $data
-     * @param array  $select_chats_params
-     *
-     * @return array
-     * @throws TelegramException
-     */
-    public static function sendToActiveChats(
-        string $callback_function,
-        array $data,
-        array $select_chats_params
-    ): array {
-        self::ensureValidAction($callback_function);
-
-        $chats = DB::selectChats($select_chats_params);
-
-        $results = [];
-        if (is_array($chats)) {
-            foreach ($chats as $row) {
-                $data['chat_id'] = $row['chat_id'];
-                $results[]       = self::send($callback_function, $data);
-            }
-        }
-
-        return $results;
-    }
-
-    /**
-     * Enable request limiter
-     *
-     * @param bool  $enable
-     * @param array $options
-     *
-     * @throws TelegramException
-     */
-    public static function setLimiter(bool $enable = true, array $options = []): void
-    {
-        if (DB::isDbConnected()) {
-            $options_default = [
-                'interval' => 1,
-            ];
-
-            $options = array_merge($options_default, $options);
-
-            if (!is_numeric($options['interval']) || $options['interval'] <= 0) {
-                throw new TelegramException('Interval must be a number and must be greater than zero!');
-            }
-
-            self::$limiter_interval = $options['interval'];
-            self::$limiter_enabled  = $enable;
-        }
-    }
-
-    /**
-     * This functions delays API requests to prevent reaching Telegram API limits
-     *  Can be disabled while in execution by 'Request::setLimiter(false)'
-     *
-     * @link https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
-     *
-     * @param string $action
-     * @param array  $data
-     *
-     * @throws TelegramException
-     */
-    private static function limitTelegramRequests(string $action, array $data = []): void
-    {
-        if (self::$limiter_enabled) {
-            $limited_methods = [
-                'sendMessage',
-                'forwardMessage',
-                'forwardMessages',
-                'copyMessage',
-                'copyMessages',
-                'sendPhoto',
-                'sendAudio',
-                'sendDocument',
-                'sendSticker',
-                'sendVideo',
-                'sendAnimation',
-                'sendVoice',
-                'sendVideoNote',
-                'sendMediaGroup',
-                'sendLocation',
-                'editMessageLiveLocation',
-                'stopMessageLiveLocation',
-                'sendVenue',
-                'sendContact',
-                'sendPoll',
-                'sendDice',
-                'setMessageReaction',
-                'sendInvoice',
-                'sendGame',
-                'setGameScore',
-                'setMyCommands',
-                'deleteMyCommands',
-                'editMessageText',
-                'editMessageCaption',
-                'editMessageMedia',
-                'editMessageReplyMarkup',
-                'stopPoll',
-                'deleteMessage',
-                'deleteMessages',
-                'setChatTitle',
-                'setChatDescription',
-                'setChatStickerSet',
-                'deleteChatStickerSet',
-                'setPassportDataErrors',
-            ];
-
-            $chat_id           = $data['chat_id'] ?? null;
-            $inline_message_id = $data['inline_message_id'] ?? null;
-
-            if (($chat_id || $inline_message_id) && in_array($action, $limited_methods, true)) {
-                $timeout = 60;
-
-                while (true) {
-                    if ($timeout <= 0) {
-                        throw new TelegramException('Timed out while waiting for a request spot!');
-                    }
-
-                    if (!($requests = DB::getTelegramRequestCount($chat_id, $inline_message_id))) {
-                        break;
-                    }
-
-                    // Make sure we're handling integers here.
-                    $requests = array_map('intval', $requests);
-
-                    $chat_per_second   = ($requests['LIMIT_PER_SEC'] === 0);    // No more than one message per second inside a particular chat
-                    $global_per_second = ($requests['LIMIT_PER_SEC_ALL'] < 30); // No more than 30 messages per second to different chats
-                    $groups_per_minute = (((is_numeric($chat_id) && $chat_id > 0) || $inline_message_id !== null) || ((!is_numeric($chat_id) || $chat_id < 0) && $requests['LIMIT_PER_MINUTE'] < 20));    // No more than 20 messages per minute in groups and channels
-
-                    if ($chat_per_second && $global_per_second && $groups_per_minute) {
-                        break;
-                    }
-
-                    $timeout--;
-                    usleep((int) (self::$limiter_interval * 1000000));
-                }
-
-                DB::insertTelegramRequest($action, $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.
-     *
-     * @deprecated
-     * @see Request::banChatMember()
-     *
-     * @param array $data
-     *
-     * @return ServerResponse
-     */
-    public static function kickChatMember(array $data = []): ServerResponse
-    {
-        return static::banChatMember($data);
-    }
-}
diff --git a/src/Telegram.php b/src/Telegram.php
index bbc948052..704ffdc09 100644
--- a/src/Telegram.php
+++ b/src/Telegram.php
@@ -1,1336 +1,418 @@
 <?php
 
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot;
-
-defined('TB_BASE_PATH') || define('TB_BASE_PATH', __DIR__);
-defined('TB_BASE_COMMANDS_PATH') || define('TB_BASE_COMMANDS_PATH', TB_BASE_PATH . '/Commands');
-
-use Exception;
-use InvalidArgumentException;
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\Commands\Command;
-use Longman\TelegramBot\Commands\SystemCommand;
-use Longman\TelegramBot\Commands\UserCommand;
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Entities\User;
-use Longman\TelegramBot\Exception\TelegramException;
-use PDO;
-use RecursiveDirectoryIterator;
-use RecursiveIteratorIterator;
-use RegexIterator;
+namespace PhpTelegramBot\Core;
+
+use PhpTelegramBot\Core\ApiMethods\AnswersInlineQueries;
+use PhpTelegramBot\Core\ApiMethods\SendsInvoices;
+use PhpTelegramBot\Core\ApiMethods\SendsMessages;
+use PhpTelegramBot\Core\ApiMethods\SendsStickers;
+use PhpTelegramBot\Core\ApiMethods\UpdatesMessages;
+use PhpTelegramBot\Core\Contracts\Factory;
+use PhpTelegramBot\Core\Entities\Update;
+use PhpTelegramBot\Core\Events\Event;
+use PhpTelegramBot\Core\Events\IncomingUpdate;
+use PhpTelegramBot\Core\Events\UnregisteredCommand;
+use PhpTelegramBot\Core\Events\UnregisteredMessageType;
+use PhpTelegramBot\Core\Events\UnregisteredUpdateType;
+use PhpTelegramBot\Core\Exceptions\InvalidArgumentException;
+use PhpTelegramBot\Core\Exceptions\TelegramException;
+use Psr\Http\Message\StreamInterface;
+
+use function PhpTelegramBot\Core\Helpers\array_is_assoc;
 
 class Telegram
 {
-    /**
-     * Version
-     *
-     * @var string
-     */
-    protected $version = '0.82.0';
-
-    /**
-     * Telegram API key
-     *
-     * @var string
-     */
-    protected $api_key = '';
-
-    /**
-     * Telegram Bot username
-     *
-     * @var string
-     */
-    protected $bot_username = '';
-
-    /**
-     * Telegram Bot id
-     *
-     * @var int
-     */
-    protected $bot_id = 0;
-
-    /**
-     * Raw request data (json) for webhook methods
-     *
-     * @var string
-     */
-    protected $input = '';
-
-    /**
-     * Custom commands paths
-     *
-     * @var array
-     */
-    protected $commands_paths = [];
-
-    /**
-     * Custom command class names
-     * ```
-     * [
-     *     'User' => [
-     *         // command_name => command_class
-     *         'start' => 'name\space\to\StartCommand',
-     *     ],
-     *     'Admin' => [], //etc
-     * ]
-     * ```
-     *
-     * @var array
-     */
-    protected $command_classes = [
-        Command::AUTH_USER   => [],
-        Command::AUTH_ADMIN  => [],
-        Command::AUTH_SYSTEM => [],
-    ];
-
-    /**
-     * Custom commands objects
-     *
-     * @var array
-     */
-    protected $commands_objects = [];
-
-    /**
-     * Current Update object
-     *
-     * @var Update
-     */
-    protected $update;
-
-    /**
-     * Upload path
-     *
-     * @var string
-     */
-    protected $upload_path = '';
-
-    /**
-     * Download path
-     *
-     * @var string
-     */
-    protected $download_path = '';
-
-    /**
-     * MySQL integration
-     *
-     * @var bool
-     */
-    protected $mysql_enabled = false;
-
-    /**
-     * PDO object
-     *
-     * @var PDO
-     */
-    protected $pdo;
-
-    /**
-     * Commands config
-     *
-     * @var array
-     */
-    protected $commands_config = [];
-
-    /**
-     * Admins list
-     *
-     * @var array
-     */
-    protected $admins_list = [];
-
-    /**
-     * ServerResponse of the last Command execution
-     *
-     * @var ServerResponse
-     */
-    protected $last_command_response;
-
-    /**
-     * Check if runCommands() is running in this session
-     *
-     * @var bool
-     */
-    protected $run_commands = false;
-
-    /**
-     * Is running getUpdates without DB enabled
-     *
-     * @var bool
-     */
-    protected $getupdates_without_database = false;
-
-    /**
-     * Last update ID
-     * Only used when running getUpdates without a database
-     *
-     * @var int
-     */
-    protected $last_update_id;
-
-    /**
-     * The command to be executed when there's a new message update and nothing more suitable is found
-     */
-    public const GENERIC_MESSAGE_COMMAND = 'genericmessage';
-
-    /**
-     * The command to be executed by default (when no other relevant commands are applicable)
-     */
-    public const GENERIC_COMMAND = 'generic';
-
-    /**
-     * Update filter method
-     *
-     * @var callable
-     */
-    protected $update_filter;
-
-    /**
-     * Telegram constructor.
-     *
-     * @param string $api_key
-     * @param string $bot_username
-     *
-     * @throws TelegramException
-     */
-    public function __construct(string $api_key, string $bot_username = '')
-    {
-        if (empty($api_key)) {
-            throw new TelegramException('API KEY not defined!');
-        }
-        preg_match('/(\d+):[\w\-]+/', $api_key, $matches);
-        if (!isset($matches[1])) {
-            throw new TelegramException('Invalid API KEY defined!');
-        }
-        $this->bot_id  = (int) $matches[1];
-        $this->api_key = $api_key;
-
-        $this->bot_username = $bot_username;
-
-        //Add default system commands path
-        $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/SystemCommands');
-
-        Request::initialize($this);
-    }
+    use AnswersInlineQueries;
+    use SendsInvoices;
+    use SendsMessages;
+    use SendsStickers;
+    use UpdatesMessages;
+
+    protected string $apiBaseUri = 'https://api.telegram.org';
+
+    public static function inputFileFields(): array
+    {
+        return [
+            'addStickerToSet'        => [
+                'sticker' => ['sticker'],
+            ],
+            'createNewStickerSet'    => [
+                'stickers' => ['sticker'],
+            ],
+            'editMessageMedia'       => [
+                'media' => ['media'],
+            ],
+            'replaceStickerInSet'    => [
+                'sticker' => ['sticker'],
+            ],
+            'sendAnimation'          => ['animation', 'thumbnail'],
+            'sendAudio'              => ['audio', 'thumbnail'],
+            'sendDocument'           => ['document', 'thumbnail'],
+            'sendMediaGroup'         => [
+                'media' => ['media', 'thumbnail'],
+            ],
+            'sendPhoto'              => ['photo'],
+            'sendSticker'            => ['sticker'],
+            'sendVideo'              => ['video', 'thumbnail'],
+            'sendVideoNote'          => ['video_note', 'thumbnail'],
+            'sendPaidMedia'          => [
+                'media' => ['media', 'thumbnail'],
+            ],
+            'sendVoice'              => ['voice'],
+            'setChatPhoto'           => ['photo'],
+            'setStickerSetThumbnail' => ['thumbnail'],
+            'setWebhook'             => ['certificate'],
+            'uploadStickerFile'      => ['sticker'],
+        ];
+    }
+    // <InputPaidMedia>.media
+
+    public function __construct(
+        #[\SensitiveParameter]
+        protected string $botToken,
+        protected ?string $botUsername = null,
+        protected ?HttpClient $client = null,
+    ) {
+        $this->client ??= new HttpClient();
+    }
+
+    public function __call(string $methodName, array $arguments): mixed
+    {
+        return $this->send($methodName, $arguments[0] ?? null, $arguments[1] ?? null);
+    }
+
+    /**
+     * @return StreamInterface[]
+     */
+    protected function extractFiles(array $fields, array &$data): array
+    {
+        $streams = [];
+        foreach ($fields as $key => $value) {
+
+            if (is_string($value)) {
+                if (! isset($data[$value])) {
+                    continue;
+                }
 
-    /**
-     * Initialize Database connection
-     *
-     * @param array  $credentials
-     * @param string $table_prefix
-     * @param string $encoding
-     *
-     * @return Telegram
-     * @throws TelegramException
-     */
-    public function enableMySql(array $credentials, string $table_prefix = '', string $encoding = 'utf8mb4'): Telegram
-    {
-        $this->pdo = DB::initialize($credentials, $this, $table_prefix, $encoding);
-        ConversationDB::initializeConversation();
-        $this->mysql_enabled = true;
+                $file = $data[$value];
 
-        return $this;
-    }
+                if (is_string($file) && is_file($file) || is_resource($file) || $file instanceof StreamInterface) {
+                    $fileId = uniqid($value . '_');
+                    $data[$value] = 'attach://' . $fileId;
 
-    /**
-     * Initialize Database external connection
-     *
-     * @param PDO    $external_pdo_connection PDO database object
-     * @param string $table_prefix
-     *
-     * @return Telegram
-     * @throws TelegramException
-     */
-    public function enableExternalMySql(PDO $external_pdo_connection, string $table_prefix = ''): Telegram
-    {
-        $this->pdo = DB::externalInitialize($external_pdo_connection, $this, $table_prefix);
-        ConversationDB::initializeConversation();
-        $this->mysql_enabled = true;
-
-        return $this;
-    }
-
-    /**
-     * Get commands list
-     *
-     * @return array $commands
-     * @throws TelegramException
-     */
-    public function getCommandsList(): array
-    {
-        $commands = [];
-
-        foreach ($this->commands_paths as $path) {
-            try {
-                //Get all "*Command.php" files
-                $files = new RegexIterator(
-                    new RecursiveIteratorIterator(
-                        new RecursiveDirectoryIterator($path)
-                    ),
-                    '/^.+Command.php$/'
-                );
-
-                foreach ($files as $file) {
-                    // Convert filename to command
-                    $command = $this->classNameToCommandName(substr($file->getFilename(), 0, -4));
-
-                    // Invalid Classname
-                    if (is_null($command)) {
-                        continue;
-                    }
+                    $streams[$fileId] = match (true) {
+                        is_string($file) && is_file($file) => $this->client->streamFactory()->createStreamFromFile($file),
+                        is_resource($file)                 => $this->client->streamFactory()->createStreamFromResource($file),
+                        $file instanceof StreamInterface   => $file,
+                    };
+                }
+            } elseif (! array_is_assoc($data[$key])) {
 
-                    // Already registered
-                    if (array_key_exists($command, $commands)) {
-                        continue;
-                    }
+                foreach ($data[$key] as &$item) {
+                    $streams += $this->extractFiles($value, $item);
+                }
 
-                    require_once $file->getPathname();
+            } else {
 
-                    $command_obj = $this->getCommandObject($command, $file->getPathname());
-                    if ($command_obj instanceof Command) {
-                        $commands[$command] = $command_obj;
-                    }
-                }
-            } catch (Exception $e) {
-                throw new TelegramException('Error getting commands from path: ' . $path, 0, $e);
+                $streams += $this->extractFiles($value, $data[$key]);
             }
         }
 
-        return $commands;
+        return $streams;
     }
 
-    /**
-     * Get classname of predefined commands
-     *
-     * @see command_classes
-     *
-     * @param string $auth     Auth of command
-     * @param string $command  Command name
-     * @param string $filepath Path to the command file
-     *
-     * @return string|null
-     */
-    public function getCommandClassName(string $auth, string $command, string $filepath = ''): ?string
+    protected function send(string $methodName, ?array $data = null, string|array|null $returnType = null): mixed
     {
-        $command = mb_strtolower($command);
-
-        // Invalid command
-        if (trim($command) === '') {
-            return null;
-        }
+        $requestUri = $this->apiBaseUri . '/bot' . $this->botToken . '/' . $methodName;
 
-        $auth    = $this->ucFirstUnicode($auth);
+        $streams = $this->extractFiles(self::inputFileFields()[$methodName] ?? [], $data);
 
-        // First, check for directly assigned command class.
-        if ($command_class = $this->command_classes[$auth][$command] ?? null) {
-            return $command_class;
-        }
+        $response = match (true) {
+            empty($data)        => $this->client->get($requestUri),
+            count($streams) > 0 => $this->client->postMultipart($requestUri, $data, $streams),
+            default             => $this->client->postJson($requestUri, $data),
+        };
 
-        // Start with default namespace.
-        $command_namespace = __NAMESPACE__ . '\\Commands\\' . $auth . 'Commands';
+        $result = json_decode($response->getBody()->getContents(), true);
 
-        // Check if we can get the namespace from the file (if passed).
-        if ($filepath && !($command_namespace = $this->getFileNamespace($filepath))) {
-            return null;
+        if ($result === null || $result['ok'] !== true) {
+            throw new TelegramException(
+                $result['description'] ?? $response->getReasonPhrase(),
+                $result['error_code'] ?? $response->getStatusCode(),
+            );
         }
 
-        $command_class = $command_namespace . '\\' . $this->commandNameToClassName($command);
-
-        if (class_exists($command_class)) {
-            return $command_class;
+        if ($returnType === null) {
+            return $result['result'];
         }
 
-        return null;
-    }
+        if (is_array($returnType)) {
+            $returnType = $returnType[0];
 
-    /**
-     * Get an object instance of the passed command
-     *
-     * @param string $command
-     * @param string $filepath
-     *
-     * @return Command|null
-     */
-    public function getCommandObject(string $command, string $filepath = ''): ?Command
-    {
-        if (isset($this->commands_objects[$command])) {
-            return $this->commands_objects[$command];
+            return array_map(fn ($item) => $this->makeResultObject($item, $returnType), $result['result']);
         }
 
-        $which = [Command::AUTH_SYSTEM];
-        $this->isAdmin() && $which[] = Command::AUTH_ADMIN;
-        $which[] = Command::AUTH_USER;
-
-        foreach ($which as $auth) {
-            $command_class = $this->getCommandClassName($auth, $command, $filepath);
-
-            if ($command_class) {
-                $command_obj = new $command_class($this, $this->update);
-
-                if ($auth === Command::AUTH_SYSTEM && $command_obj instanceof SystemCommand) {
-                    return $command_obj;
-                }
-                if ($auth === Command::AUTH_ADMIN && $command_obj instanceof AdminCommand) {
-                    return $command_obj;
-                }
-                if ($auth === Command::AUTH_USER && $command_obj instanceof UserCommand) {
-                    return $command_obj;
-                }
-            }
-        }
-
-        return null;
+        return $this->makeResultObject($result['result'], $returnType);
     }
 
-    /**
-     * Get namespace from php file by src path
-     *
-     * @param string $src (absolute path to file)
-     *
-     * @return string|null ("Longman\TelegramBot\Commands\SystemCommands" for example)
-     */
-    protected function getFileNamespace(string $src): ?string
+    protected function makeResultObject(mixed $result, string|array|null $returnType = null): mixed
     {
-        $content = file_get_contents($src);
-        if (preg_match('#^\s*namespace\s+(.+?);#m', $content, $m)) {
-            return $m[1];
+        if (! is_array($result)) {
+            return $result;
         }
 
-        return null;
-    }
-
-    /**
-     * Set custom input string for debug purposes
-     *
-     * @param string $input (json format)
-     *
-     * @return Telegram
-     */
-    public function setCustomInput(string $input): Telegram
-    {
-        $this->input = $input;
-
-        return $this;
-    }
-
-    /**
-     * Get custom input string for debug purposes
-     *
-     * @return string
-     */
-    public function getCustomInput(): string
-    {
-        return $this->input;
-    }
+        if (is_subclass_of($returnType, Factory::class)) {
+            return $returnType::make($result);
+        }
 
-    /**
-     * Get the ServerResponse of the last Command execution
-     *
-     * @return ServerResponse
-     */
-    public function getLastCommandResponse(): ServerResponse
-    {
-        return $this->last_command_response;
+        return new $returnType($result);
     }
 
-    /**
-     * Handle getUpdates method
-     *
-     * @todo Remove backwards compatibility for old signature and force $data to be an array.
-     *
-     * @param array|int|null $data
-     * @param int|null       $timeout
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function handleGetUpdates($data = null, ?int $timeout = null): ServerResponse
+    public function handleGetUpdates(int $pollingInterval = 30, ?array $allowedUpdates = null)
     {
-        if (empty($this->bot_username)) {
-            throw new TelegramException('Bot Username is not defined!');
-        }
-
-        if (!DB::isDbConnected() && !$this->getupdates_without_database) {
-            return new ServerResponse(
-                [
-                    'ok'          => false,
-                    'description' => 'getUpdates needs MySQL connection! (This can be overridden - see documentation)',
-                ],
-                $this->bot_username
-            );
-        }
-
-        $offset = 0;
-        $limit  = null;
-
-        // By default, get update types sent by Telegram.
-        $allowed_updates = [];
-
-        // @todo Backwards compatibility for old signature, remove in next version.
-        if (!is_array($data)) {
-            $limit = $data;
-
-            @trigger_error(
-                sprintf('Use of $limit and $timeout parameters in %s is deprecated. Use $data array instead.', __METHOD__),
-                E_USER_DEPRECATED
-            );
-        } else {
-            $offset          = $data['offset'] ?? $offset;
-            $limit           = $data['limit'] ?? $limit;
-            $timeout         = $data['timeout'] ?? $timeout;
-            $allowed_updates = $data['allowed_updates'] ?? $allowed_updates;
-        }
-
-        // Take custom input into account.
-        if ($custom_input = $this->getCustomInput()) {
-            try {
-                $input = json_decode($this->input, true, 512, JSON_THROW_ON_ERROR);
-                if (empty($input)) {
-                    throw new TelegramException('Custom input is empty');
-                }
-                $response = new ServerResponse($input, $this->bot_username);
-            } catch (\Throwable $e) {
-                throw new TelegramException('Invalid custom input JSON: ' . $e->getMessage());
-            }
-        } else {
-            if (DB::isDbConnected() && $last_update = DB::selectTelegramUpdate(1)) {
-                // Get last Update id from the database.
-                $last_update          = reset($last_update);
-                $this->last_update_id = $last_update['id'] ?? null;
-            }
-
-            if ($this->last_update_id !== null) {
-                $offset = $this->last_update_id + 1; // As explained in the telegram bot API documentation.
-            }
-
-            $response = Request::getUpdates(compact('offset', 'limit', 'timeout', 'allowed_updates'));
-        }
-
-        if ($response->isOk()) {
-            // Log update.
-            TelegramLog::update($response->toJson());
+        $offset = null;
+        while (true) {
+            $updates = $this->getUpdates([
+                'offset'          => $offset,
+                'timeout'         => $pollingInterval,
+                'allowed_updates' => $allowedUpdates,
+            ]);
 
-            // Process all updates
-            /** @var Update $update */
-            foreach ($response->getResult() as $update) {
+            foreach ($updates as $update) {
                 $this->processUpdate($update);
-            }
-
-            if (!DB::isDbConnected() && !$custom_input && $this->last_update_id !== null && $offset === 0) {
-                // Mark update(s) as read after handling
-                $offset = $this->last_update_id + 1;
-                $limit  = 1;
-
-                Request::getUpdates(compact('offset', 'limit', 'timeout', 'allowed_updates'));
+                $offset = $update->getUpdateId() + 1;
             }
         }
-
-        return $response;
     }
 
-    /**
-     * Handle bot request from webhook
-     *
-     * @return bool
-     *
-     * @throws TelegramException
-     */
-    public function handle(): bool
+    public function handle()
     {
-        if ($this->bot_username === '') {
-            throw new TelegramException('Bot Username is not defined!');
-        }
+        $data = file_get_contents('php://input');
 
-        $input = Request::getInput();
-        if (empty($input)) {
-            throw new TelegramException('Input is empty! The webhook must not be called manually, only by Telegram.');
+        if (empty($data)) {
+            return;
         }
 
-        // Log update.
-        TelegramLog::update($input);
-
-        $post = json_decode($input, true);
-        if (empty($post)) {
-            throw new TelegramException('Invalid input JSON! The webhook must not be called manually, only by Telegram.');
-        }
+        $json = json_decode($data, true);
 
-        if ($response = $this->processUpdate(new Update($post, $this->bot_username))) {
-            return $response->isOk();
-        }
+        $update = new Update($json);
 
-        return false;
+        $this->processUpdate($update);
     }
 
-    /**
-     * Get the command name from the command type
-     *
-     * @param string $type
-     *
-     * @return string
-     */
-    protected function getCommandFromType(string $type): string
+    protected function dispatch(Event $event)
     {
-        return $this->ucFirstUnicode(str_replace('_', '', $type));
+        $listeners = $this->eventListeners[$event::class] ?? [];
+
+        foreach ($listeners as $callback) {
+            $callback($event, $this);
+        }
     }
 
-    /**
-     * Process bot Update request
-     *
-     * @param Update $update
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function processUpdate(Update $update): ServerResponse
+    protected function processUpdate(Update $update)
     {
-        $this->update         = $update;
-        $this->last_update_id = $update->getUpdateId();
-
-        if (is_callable($this->update_filter)) {
-            $reason = 'Update denied by update_filter';
-            try {
-                $allowed = (bool) call_user_func_array($this->update_filter, [$update, $this, &$reason]);
-            } catch (Exception $e) {
-                $allowed = false;
-            }
+        // IncomingUpdate Event
+        $this->dispatch(new IncomingUpdate($update));
 
-            if (!$allowed) {
-                TelegramLog::debug($reason);
-                return new ServerResponse(['ok' => false, 'description' => 'denied']);
+        // Update Types
+        $registeredUpdateType = false;
+        foreach ($this->updateTypeCallbacks as $key => $callbacks) {
+            if (! is_null($update->$key)) {
+                $registeredUpdateType = true;
+                foreach ($callbacks as $callback) {
+                    $callback($update, $this);
+                }
             }
         }
 
-        //Load admin commands
-        if ($this->isAdmin()) {
-            $this->addCommandsPath(TB_BASE_COMMANDS_PATH . '/AdminCommands', false);
+        if (! $registeredUpdateType) {
+            $this->dispatch(new UnregisteredUpdateType($update));
         }
 
-        //Make sure we have an up-to-date command list
-        //This is necessary to "require" all the necessary command files!
-        $this->commands_objects = $this->getCommandsList();
-
-        //If all else fails, it's a generic message.
-        $command = self::GENERIC_MESSAGE_COMMAND;
-
-        $update_type = $this->update->getUpdateType();
-        if ($update_type === 'message') {
-            $message = $this->update->getMessage();
-            $type    = $message->getType();
-
-            // Let's check if the message object has the type field we're looking for...
-            $command_tmp = $type === 'command' ? $message->getCommand() : $this->getCommandFromType($type);
-            // ...and if a fitting command class is available.
-            $command_obj = $command_tmp ? $this->getCommandObject($command_tmp) : null;
-
-            // Empty usage string denotes a non-executable command.
-            // @see https://github.com/php-telegram-bot/core/issues/772#issuecomment-388616072
-            if (
-                ($command_obj === null && $type === 'command')
-                || ($command_obj !== null && $command_obj->getUsage() !== '')
-            ) {
-                $command = $command_tmp;
+        // Message Types
+        $registeredMessageType = false;
+        $message = $update->getMessage();
+        if ($message !== null) {
+            foreach ($this->messageTypeCallbacks as $key => $callbacks) {
+                if (! is_null($message->$key)) {
+                    $registeredMessageType = true;
+                    foreach ($callbacks as $callback) {
+                        $callback($update, $this);
+                    }
+                }
             }
-        } elseif ($update_type !== null) {
-            $command = $this->getCommandFromType($update_type);
         }
 
-        //Make sure we don't try to process update that was already processed
-        $last_id = DB::selectTelegramUpdate(1, $this->update->getUpdateId());
-        if ($last_id && count($last_id) === 1) {
-            TelegramLog::debug('Duplicate update received, processing aborted!');
-            return Request::emptyResponse();
+        if (! $registeredMessageType) {
+            $this->dispatch(new UnregisteredMessageType($update));
         }
 
-        DB::insertRequest($this->update);
+        // Commands
+        $registeredCommand = false;
+        $command = $update->getMessage()?->getText() ?? null;
+        if ($command !== null && str_starts_with($command, '/')) {
+            // Cut / from beginning
+            $command = ltrim($command, '/');
 
-        return $this->executeCommand($command);
-    }
+            // Cut username
+            $command = explode(' ', $command, 2)[0];
+            [$command, $username] = explode('@', $command, 2) + [null, null];
 
-    /**
-     * Execute /command
-     *
-     * @param string $command
-     *
-     * @return ServerResponse
-     * @throws TelegramException
-     */
-    public function executeCommand(string $command): ServerResponse
-    {
-        $command = mb_strtolower($command);
+            // TODO: Check if the username belongs to this bot...
 
-        $command_obj = $this->commands_objects[$command] ?? $this->getCommandObject($command);
+            // Get callbacks
+            $callbacks = $this->commandCallbacks[$command] ?? [];
 
-        if (!$command_obj || !$command_obj->isEnabled()) {
-            //Failsafe in case the Generic command can't be found
-            if ($command === self::GENERIC_COMMAND) {
-                throw new TelegramException('Generic command missing!');
+            if (! empty($callbacks)) {
+                $registeredCommand = true;
             }
 
-            //Handle a generic command or non existing one
-            $this->last_command_response = $this->executeCommand(self::GENERIC_COMMAND);
-        } else {
-            //execute() method is executed after preExecute()
-            //This is to prevent executing a DB query without a valid connection
-            if ($this->update) {
-                $this->last_command_response = $command_obj->setUpdate($this->update)->preExecute();
-            } else {
-                $this->last_command_response = $command_obj->preExecute();
+            foreach ($callbacks as $callback) {
+                $callback($update, $this);
             }
         }
 
-        return $this->last_command_response;
-    }
-
-    /**
-     * @deprecated
-     *
-     * @param string $command
-     *
-     * @return string
-     */
-    protected function sanitizeCommand(string $command): string
-    {
-        return str_replace(' ', '', $this->ucWordsUnicode(str_replace('_', ' ', $command)));
-    }
-
-    /**
-     * Enable a single Admin account
-     *
-     * @param int $admin_id Single admin id
-     *
-     * @return Telegram
-     */
-    public function enableAdmin(int $admin_id): Telegram
-    {
-        if ($admin_id <= 0) {
-            TelegramLog::error('Invalid value "' . $admin_id . '" for admin.');
-        } elseif (!in_array($admin_id, $this->admins_list, true)) {
-            $this->admins_list[] = $admin_id;
-        }
-
-        return $this;
-    }
-
-    /**
-     * Enable a list of Admin Accounts
-     *
-     * @param array $admin_ids List of admin ids
-     *
-     * @return Telegram
-     */
-    public function enableAdmins(array $admin_ids): Telegram
-    {
-        foreach ($admin_ids as $admin_id) {
-            $this->enableAdmin($admin_id);
+        if (! $registeredCommand) {
+            $this->dispatch(new UnregisteredCommand($update));
         }
-
-        return $this;
     }
 
     /**
-     * Get list of admins
-     *
-     * @return array
+     * @var array<class-string<Event>, array<callable>>
      */
-    public function getAdminList(): array
-    {
-        return $this->admins_list;
-    }
+    protected array $eventListeners = [];
 
     /**
-     * Check if the passed user is an admin
-     *
-     * If no user id is passed, the current update is checked for a valid message sender.
-     *
-     * @param int|null $user_id
-     *
-     * @return bool
+     * @param  class-string<Event>      $event
+     * @param  callable|array<callable> $callback
+     * @return $this
      */
-    public function isAdmin($user_id = null): bool
+    public function registerEventListener(string $event, callable|array $callback): static
     {
-        if ($user_id === null && $this->update !== null) {
-            //Try to figure out if the user is an admin
-            $update_methods = [
-                'getMessage',
-                'getEditedMessage',
-                'getChannelPost',
-                'getEditedChannelPost',
-                'getInlineQuery',
-                'getChosenInlineResult',
-                'getCallbackQuery',
-            ];
-            foreach ($update_methods as $update_method) {
-                $object = call_user_func([$this->update, $update_method]);
-                if ($object !== null && $from = $object->getFrom()) {
-                    $user_id = $from->getId();
-                    break;
-                }
-            }
+        if (is_callable($callback)) {
+            $callback = [$callback];
         }
 
-        return ($user_id === null) ? false : in_array($user_id, $this->admins_list, true);
-    }
-
-    /**
-     * Check if user required the db connection
-     *
-     * @return bool
-     */
-    public function isDbEnabled(): bool
-    {
-        return $this->mysql_enabled;
-    }
-
-    /**
-     * Add a single custom command class
-     *
-     * @param string $command_class Full command class name
-     *
-     * @return Telegram
-     */
-    public function addCommandClass(string $command_class): Telegram
-    {
-        if (!$command_class || !class_exists($command_class)) {
-            $error = sprintf('Command class "%s" does not exist.', $command_class);
-            TelegramLog::error($error);
-            throw new InvalidArgumentException($error);
-        }
-
-        if (!is_a($command_class, Command::class, true)) {
-            $error = sprintf('Command class "%s" does not extend "%s".', $command_class, Command::class);
-            TelegramLog::error($error);
-            throw new InvalidArgumentException($error);
+        if (! is_subclass_of($event, Event::class)) {
+            throw new InvalidArgumentException("$event is not a PhpTelegramBot Event");
         }
 
-        // Dummy object to get data from.
-        $command_object = new $command_class($this);
-
-        $auth = null;
-        $command_object->isSystemCommand() && $auth = Command::AUTH_SYSTEM;
-        $command_object->isAdminCommand() && $auth = Command::AUTH_ADMIN;
-        $command_object->isUserCommand() && $auth = Command::AUTH_USER;
-
-        if ($auth) {
-            $command = mb_strtolower($command_object->getName());
-
-            $this->command_classes[$auth][$command] = $command_class;
-        }
+        $this->eventListeners[$event] = array_merge($this->eventListeners[$event] ?? [], $callback);
 
         return $this;
     }
 
     /**
-     * Add multiple custom command classes
-     *
-     * @param array $command_classes List of full command class names
-     *
-     * @return Telegram
+     * @param  array<class-string<Event>, callable|array<callable>> $callbacks
+     * @return $this
      */
-    public function addCommandClasses(array $command_classes): Telegram
+    public function registerEventListeners(array $callbacks): static
     {
-        foreach ($command_classes as $command_class) {
-            $this->addCommandClass($command_class);
+        foreach ($callbacks as $event => $callback) {
+            $this->registerEventListener($event, $callback);
         }
 
         return $this;
     }
 
     /**
-     * Set a single custom commands path
-     *
-     * @param string $path Custom commands path to set
-     *
-     * @return Telegram
+     * @var array<string, array<callable>>
      */
-    public function setCommandsPath(string $path): Telegram
-    {
-        $this->commands_paths = [];
-
-        $this->addCommandsPath($path);
-
-        return $this;
-    }
+    protected array $updateTypeCallbacks = [];
 
     /**
-     * Add a single custom commands path
-     *
-     * @param string $path   Custom commands path to add
-     * @param bool   $before If the path should be prepended or appended to the list
-     *
-     * @return Telegram
+     * @param  callable|array<callable> $callback
+     * @return $this
      */
-    public function addCommandsPath(string $path, bool $before = true): Telegram
+    public function registerUpdateType(string $updateType, callable|array $callback): static
     {
-        if (!is_dir($path)) {
-            TelegramLog::error('Commands path "' . $path . '" does not exist.');
-        } elseif (!in_array($path, $this->commands_paths, true)) {
-            if ($before) {
-                array_unshift($this->commands_paths, $path);
-            } else {
-                $this->commands_paths[] = $path;
-            }
+        if (is_callable($callback)) {
+            $callback = [$callback];
         }
 
-        return $this;
-    }
-
-    /**
-     * Set multiple custom commands paths
-     *
-     * @param array $paths Custom commands paths to add
-     *
-     * @return Telegram
-     */
-    public function setCommandsPaths(array $paths): Telegram
-    {
-        $this->commands_paths = [];
-
-        $this->addCommandsPaths($paths);
+        $this->updateTypeCallbacks[$updateType] = array_merge($this->updateTypeCallbacks[$updateType] ?? [], $callback);
 
         return $this;
     }
 
     /**
-     * Add multiple custom commands paths
-     *
-     * @param array $paths  Custom commands paths to add
-     * @param bool  $before If the paths should be prepended or appended to the list
-     *
-     * @return Telegram
+     * @param  array<string, callable|array<callable>> $callbacks
+     * @return $this
      */
-    public function addCommandsPaths(array $paths, bool $before = true): Telegram
+    public function registerUpdateTypes(array $callbacks): Telegram
     {
-        foreach ($paths as $path) {
-            $this->addCommandsPath($path, $before);
+        foreach ($callbacks as $type => $callback) {
+            $this->registerUpdateType($type, $callback);
         }
 
         return $this;
     }
 
     /**
-     * Return the list of commands paths
-     *
-     * @return array
-     */
-    public function getCommandsPaths(): array
-    {
-        return $this->commands_paths;
-    }
-
-    /**
-     * Return the list of command classes
-     *
-     * @return array
-     */
-    public function getCommandClasses(): array
-    {
-        return $this->command_classes;
-    }
-
-    /**
-     * Set custom upload path
-     *
-     * @param string $path Custom upload path
-     *
-     * @return Telegram
+     * @var array<string, array<callable>>
      */
-    public function setUploadPath(string $path): Telegram
-    {
-        $this->upload_path = $path;
-
-        return $this;
-    }
+    protected array $messageTypeCallbacks = [];
 
     /**
-     * Get custom upload path
-     *
-     * @return string
+     * @param  callable|array<callable> $callback
+     * @return $this
      */
-    public function getUploadPath(): string
+    public function registerMessageType(string $messageType, array|callable $callback): static
     {
-        return $this->upload_path;
-    }
+        if (is_callable($callback)) {
+            $callback = [$callback];
+        }
 
-    /**
-     * Set custom download path
-     *
-     * @param string $path Custom download path
-     *
-     * @return Telegram
-     */
-    public function setDownloadPath(string $path): Telegram
-    {
-        $this->download_path = $path;
+        $this->messageTypeCallbacks[$messageType] = array_merge($this->messageTypeCallbacks[$messageType] ?? [], $callback);
 
         return $this;
     }
 
     /**
-     * Get custom download path
-     *
-     * @return string
+     * @param  array<string, callable|array<callable>> $callbacks
+     * @return $this
      */
-    public function getDownloadPath(): string
+    public function registerMessageTypes(array $callbacks): static
     {
-        return $this->download_path;
-    }
-
-    /**
-     * Set command config
-     *
-     * Provide further variables to a particular commands.
-     * For example you can add the channel name at the command /sendtochannel
-     * Or you can add the api key for external service.
-     *
-     * @param string $command
-     * @param array  $config
-     *
-     * @return Telegram
-     */
-    public function setCommandConfig(string $command, array $config): Telegram
-    {
-        $this->commands_config[$command] = $config;
+        foreach ($callbacks as $messageType => $callback) {
+            $this->registerMessageType($messageType, $callback);
+        }
 
         return $this;
     }
 
     /**
-     * Get command config
-     *
-     * @param string $command
-     *
-     * @return array
-     */
-    public function getCommandConfig(string $command): array
-    {
-        return $this->commands_config[$command] ?? [];
-    }
-
-    /**
-     * Get API key
-     *
-     * @return string
-     */
-    public function getApiKey(): string
-    {
-        return $this->api_key;
-    }
-
-    /**
-     * Get Bot name
-     *
-     * @return string
-     */
-    public function getBotUsername(): string
-    {
-        return $this->bot_username;
-    }
-
-    /**
-     * Get Bot Id
-     *
-     * @return int
-     */
-    public function getBotId(): int
-    {
-        return $this->bot_id;
-    }
-
-    /**
-     * Get Version
-     *
-     * @return string
-     */
-    public function getVersion(): string
-    {
-        return $this->version;
-    }
-
-    /**
-     * Set Webhook for bot
-     *
-     * @param string $url
-     * @param array  $data Optional parameters.
-     *
-     * @return ServerResponse
-     * @throws TelegramException
+     * @var array<string, array<callable>>
      */
-    public function setWebhook(string $url, array $data = []): ServerResponse
-    {
-        if ($url === '') {
-            throw new TelegramException('Hook url is empty!');
-        }
-
-        $data        = array_intersect_key($data, array_flip([
-            'certificate',
-            'ip_address',
-            'max_connections',
-            'allowed_updates',
-            'drop_pending_updates',
-            'secret_token',
-        ]));
-        $data['url'] = $url;
-
-        // If the certificate is passed as a path, encode and add the file to the data array.
-        if (!empty($data['certificate']) && is_string($data['certificate'])) {
-            $data['certificate'] = Request::encodeFile($data['certificate']);
-        }
-
-        $result = Request::setWebhook($data);
-
-        if (!$result->isOk()) {
-            throw new TelegramException(
-                'Webhook was not set! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
-            );
-        }
-
-        return $result;
-    }
+    protected array $commandCallbacks = [];
 
     /**
-     * Delete any assigned webhook
-     *
-     * @param array $data
-     *
-     * @return ServerResponse
-     * @throws TelegramException
+     * @param  callable|array<callable> $callback
+     * @return $this
      */
-    public function deleteWebhook(array $data = []): ServerResponse
+    public function registerCommand(string $command, callable|array $callback): static
     {
-        $result = Request::deleteWebhook($data);
+        $command = ltrim($command, '/');
 
-        if (!$result->isOk()) {
-            throw new TelegramException(
-                'Webhook was not deleted! Error: ' . $result->getErrorCode() . ' ' . $result->getDescription()
-            );
+        if (is_callable($callback)) {
+            $callback = [$callback];
         }
 
-        return $result;
-    }
-
-    /**
-     * Replace function `ucwords` for UTF-8 characters in the class definition and commands
-     *
-     * @param string $str
-     * @param string $encoding (default = 'UTF-8')
-     *
-     * @return string
-     */
-    protected function ucWordsUnicode(string $str, string $encoding = 'UTF-8'): string
-    {
-        return mb_convert_case($str, MB_CASE_TITLE, $encoding);
-    }
-
-    /**
-     * Replace function `ucfirst` for UTF-8 characters in the class definition and commands
-     *
-     * @param string $str
-     * @param string $encoding (default = 'UTF-8')
-     *
-     * @return string
-     */
-    protected function ucFirstUnicode(string $str, string $encoding = 'UTF-8'): string
-    {
-        return mb_strtoupper(mb_substr($str, 0, 1, $encoding), $encoding)
-            . mb_strtolower(mb_substr($str, 1, mb_strlen($str), $encoding), $encoding);
-    }
-
-    /**
-     * Enable requests limiter
-     *
-     * @param array $options
-     *
-     * @return Telegram
-     * @throws TelegramException
-     */
-    public function enableLimiter(array $options = []): Telegram
-    {
-        Request::setLimiter(true, $options);
+        $this->commandCallbacks[$command] = array_merge($this->commandCallbacks[$command] ?? [], $callback);
 
         return $this;
     }
 
     /**
-     * Run provided commands
-     *
-     * @param array $commands
-     *
-     * @return ServerResponse[]
-     *
-     * @throws TelegramException
+     * @param  array<string, callable|array<callable>> $callbacks
+     * @return $this
      */
-    public function runCommands(array $commands): array
+    public function registerCommands(array $callbacks): static
     {
-        if (empty($commands)) {
-            throw new TelegramException('No command(s) provided!');
-        }
-
-        $this->run_commands = true;
-
-        // Check if this request has a user Update / comes from Telegram.
-        if ($userUpdate = $this->update) {
-            $from = $this->update->getMessage()->getFrom();
-            $chat = $this->update->getMessage()->getChat();
-        } else {
-            // Fall back to the Bot user.
-            $from = new User([
-                'id'         => $this->getBotId(),
-                'first_name' => $this->getBotUsername(),
-                'username'   => $this->getBotUsername(),
-            ]);
-
-            // Try to get "live" Bot info.
-            $response = Request::getMe();
-            if ($response->isOk()) {
-                /** @var User $result */
-                $result = $response->getResult();
-
-                $from = new User([
-                    'id'         => $result->getId(),
-                    'first_name' => $result->getFirstName(),
-                    'username'   => $result->getUsername(),
-                ]);
-            }
-
-            // Give Bot access to admin commands.
-            $this->enableAdmin($from->getId());
-
-            // Lock the bot to a private chat context.
-            $chat = new Chat([
-                'id'   => $from->getId(),
-                'type' => 'private',
-            ]);
-        }
-
-        $newUpdate = static function ($text = '') use ($from, $chat) {
-            return new Update([
-                'update_id' => -1,
-                'message'   => [
-                    'message_id' => -1,
-                    'date'       => time(),
-                    'from'       => json_decode($from->toJson(), true),
-                    'chat'       => json_decode($chat->toJson(), true),
-                    'text'       => $text,
-                ],
-            ]);
-        };
-
-        $responses = [];
-
-        foreach ($commands as $command) {
-            $this->update = $newUpdate($command);
-
-            // Refresh commands list for new Update object.
-            $this->commands_objects = $this->getCommandsList();
-
-            $responses[] = $this->executeCommand($this->update->getMessage()->getCommand());
+        foreach ($callbacks as $command => $callback) {
+            $this->registerCommand($command, $callback);
         }
 
-        // Reset Update to initial context.
-        $this->update = $userUpdate;
-
-        return $responses;
-    }
-
-    /**
-     * Is this session initiated by runCommands()
-     *
-     * @return bool
-     */
-    public function isRunCommands(): bool
-    {
-        return $this->run_commands;
-    }
-
-    /**
-     * Switch to enable running getUpdates without a database
-     *
-     * @param bool $enable
-     *
-     * @return Telegram
-     */
-    public function useGetUpdatesWithoutDatabase(bool $enable = true): Telegram
-    {
-        $this->getupdates_without_database = $enable;
-
         return $this;
     }
-
-    /**
-     * Return last update id
-     *
-     * @return int|null
-     */
-    public function getLastUpdateId(): ?int
-    {
-        return $this->last_update_id;
-    }
-
-    /**
-     * Set an update filter callback
-     *
-     * @param callable $callback
-     *
-     * @return Telegram
-     */
-    public function setUpdateFilter(callable $callback): Telegram
-    {
-        $this->update_filter = $callback;
-
-        return $this;
-    }
-
-    /**
-     * Return update filter callback
-     *
-     * @return callable|null
-     */
-    public function getUpdateFilter(): ?callable
-    {
-        return $this->update_filter;
-    }
-
-    /**
-     * Converts the name of a class into the name of a command.
-     *
-     * @param string $class For example FooBarCommand
-     *
-     * @return string|null for example foo_bar. In case of errors, returns null.
-     */
-    protected function classNameToCommandName(string $class): ?string
-    {
-        // If $class doesn't end with 'Command'
-        if (substr($class, -7) !== 'Command') {
-            return null;
-        }
-
-        return mb_strtolower(preg_replace('/(.)(?=[\p{Lu}])/u', '$1_', substr($class, 0, -7)));
-    }
-
-    /**
-     * Converts a command name into the name of a class.
-     *
-     * @param string $command For example foo_bar
-     *
-     * @return string|null for example FooBarCommand. In case of errors, returns null.
-     */
-    protected function commandNameToClassName(string $command): ?string
-    {
-        if (trim($command) === '') {
-            return null;
-        }
-
-        return str_replace(' ', '', $this->ucWordsUnicode(str_replace('_', ' ', $command))) . 'Command';
-    }
 }
diff --git a/src/TelegramLog.php b/src/TelegramLog.php
deleted file mode 100644
index 449256ee9..000000000
--- a/src/TelegramLog.php
+++ /dev/null
@@ -1,168 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot;
-
-use Psr\Log\LoggerInterface;
-use Psr\Log\NullLogger;
-
-/**
- * Class TelegramLog
- *
- * @method static void emergency(string $message, array $context = [])
- * @method static void alert(string $message, array $context = [])
- * @method static void critical(string $message, array $context = [])
- * @method static void error(string $message, array $context = [])
- * @method static void warning(string $message, array $context = [])
- * @method static void notice(string $message, array $context = [])
- * @method static void info(string $message, array $context = [])
- * @method static void debug(string $message, array $context = [])
- * @method static void update(string $message, array $context = [])
- */
-class TelegramLog
-{
-    /**
-     * Logger instance
-     *
-     * @var LoggerInterface
-     */
-    protected static $logger;
-
-    /**
-     * Logger instance for update
-     *
-     * @var LoggerInterface
-     */
-    protected static $update_logger;
-
-    /**
-     * Always log the request and response data to the debug log, also for successful requests
-     *
-     * @var bool
-     */
-    public static $always_log_request_and_response = false;
-
-    /**
-     * Temporary stream handle for debug log
-     *
-     * @var resource|null
-     */
-    protected static $debug_log_temp_stream_handle;
-
-    /**
-     * Remove bot token from debug stream
-     *
-     * @var bool
-     */
-    public static $remove_bot_token = true;
-
-    /**
-     * Initialise logging.
-     *
-     * @param LoggerInterface|null $logger
-     * @param LoggerInterface|null $update_logger
-     */
-    public static function initialize(LoggerInterface $logger = null, LoggerInterface $update_logger = null): void
-    {
-        self::$logger        = $logger ?: new NullLogger();
-        self::$update_logger = $update_logger ?: new NullLogger();
-    }
-
-    /**
-     * Get the stream handle of the temporary debug output
-     *
-     * @return mixed The stream if debug is active, else false
-     */
-    public static function getDebugLogTempStream()
-    {
-        if ((self::$debug_log_temp_stream_handle === null) && $temp_stream_handle = fopen('php://temp', 'wb+')) {
-            self::$debug_log_temp_stream_handle = $temp_stream_handle;
-        }
-
-        return self::$debug_log_temp_stream_handle;
-    }
-
-    /**
-     * Write the temporary debug stream to log and close the stream handle
-     *
-     * @param string $message Message (with placeholder) to write to the debug log
-     */
-    public static function endDebugLogTempStream($message = '%s'): void
-    {
-        if (is_resource(self::$debug_log_temp_stream_handle)) {
-            rewind(self::$debug_log_temp_stream_handle);
-            $stream_contents = stream_get_contents(self::$debug_log_temp_stream_handle);
-
-            if (self::$remove_bot_token) {
-                $stream_contents = preg_replace('/\/bot(\d+):[\w\-]+\//', '/botBOT_TOKEN_REMOVED/', $stream_contents);
-            }
-
-            self::debug(sprintf($message, $stream_contents));
-            fclose(self::$debug_log_temp_stream_handle);
-            self::$debug_log_temp_stream_handle = null;
-        }
-    }
-
-    /**
-     * Handle any logging method call.
-     *
-     * @param string $name
-     * @param array  $arguments
-     */
-    public static function __callStatic(string $name, array $arguments)
-    {
-        // Get the correct logger instance.
-        $logger = null;
-        if (in_array($name, ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug',], true)) {
-            $logger = self::$logger;
-        } elseif ($name === 'update') {
-            $logger = self::$update_logger;
-            $name   = 'info';
-        }
-
-        // Clearly we have no logging enabled.
-        if ($logger === null) {
-            return;
-        }
-
-        // Replace any placeholders from the passed context.
-        if (count($arguments) >= 2) {
-            $arguments[0] = self::interpolate($arguments[0], $arguments[1]);
-        }
-
-        call_user_func_array([$logger, $name], $arguments);
-    }
-
-    /**
-     * Interpolates context values into the message placeholders.
-     *
-     * @see https://www.php-fig.org/psr/psr-3/#12-message
-     *
-     * @param string $message
-     * @param array  $context
-     *
-     * @return string
-     */
-    protected static function interpolate(string $message, array $context = []): string
-    {
-        // Build a replacement array with braces around the context keys.
-        $replace = [];
-        foreach ($context as $key => $val) {
-            // check that the value can be casted to string
-            if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
-                $replace["{{$key}}"] = $val;
-            }
-        }
-
-        // Interpolate replacement values into the message and return.
-        return strtr($message, $replace);
-    }
-}
diff --git a/src/helpers.php b/src/helpers.php
new file mode 100644
index 000000000..0c1d793f2
--- /dev/null
+++ b/src/helpers.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace PhpTelegramBot\Core\Helpers;
+
+function array_is_assoc(array $array): bool
+{
+    $keys = array_keys($array);
+
+    return array_keys($keys) !== $keys;
+}
diff --git a/structure.sql b/structure.sql
deleted file mode 100644
index cf8061536..000000000
--- a/structure.sql
+++ /dev/null
@@ -1,438 +0,0 @@
-CREATE TABLE IF NOT EXISTS `user` (
-  `id` bigint COMMENT 'Unique identifier for this user or bot',
-  `is_bot` tinyint(1) DEFAULT 0 COMMENT 'True, if this user is a bot',
-  `first_name` CHAR(255) NOT NULL DEFAULT '' COMMENT 'User''s or bot''s first name',
-  `last_name` CHAR(255) DEFAULT NULL COMMENT 'User''s or bot''s last name',
-  `username` CHAR(191) DEFAULT NULL COMMENT 'User''s or bot''s username',
-  `language_code` CHAR(10) DEFAULT NULL COMMENT 'IETF language tag of the user''s language',
-  `is_premium` tinyint(1) DEFAULT 0 COMMENT 'True, if this user is a Telegram Premium user',
-  `added_to_attachment_menu` tinyint(1) DEFAULT 0 COMMENT 'True, if this user added the bot to the attachment menu',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-  `updated_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date update',
-
-  PRIMARY KEY (`id`),
-  KEY `username` (`username`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `chat` (
-  `id` bigint COMMENT 'Unique identifier for this chat',
-  `type` ENUM('private', 'group', 'supergroup', 'channel') NOT NULL COMMENT 'Type of chat, can be either private, group, supergroup or channel',
-  `title` CHAR(255) DEFAULT '' COMMENT 'Title, for supergroups, channels and group chats',
-  `username` CHAR(255) DEFAULT NULL COMMENT 'Username, for private chats, supergroups and channels if available',
-  `first_name` CHAR(255) DEFAULT NULL COMMENT 'First name of the other party in a private chat',
-  `last_name` CHAR(255) DEFAULT NULL COMMENT 'Last name of the other party in a private chat',
-  `is_forum` TINYINT(1) DEFAULT 0 COMMENT 'True, if the supergroup chat is a forum (has topics enabled)',
-  `all_members_are_administrators` tinyint(1) DEFAULT 0 COMMENT 'True if a all members of this group are admins',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-  `updated_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date update',
-  `old_id` bigint DEFAULT NULL COMMENT 'Unique chat identifier, this is filled when a group is converted to a supergroup',
-
-  PRIMARY KEY (`id`),
-  KEY `old_id` (`old_id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `user_chat` (
-  `user_id` bigint COMMENT 'Unique user identifier',
-  `chat_id` bigint COMMENT 'Unique user or chat identifier',
-
-  PRIMARY KEY (`user_id`, `chat_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
-  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',
-  `location` CHAR(255) NULL DEFAULT NULL COMMENT 'Location of the user',
-  `query` TEXT NOT NULL COMMENT 'Text of the query',
-  `offset` CHAR(255) NULL DEFAULT NULL COMMENT 'Offset of the result',
-  `chat_type` CHAR(255) NULL DEFAULT NULL COMMENT 'Optional. Type of the chat, from which the inline query was sent.',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `chosen_inline_result` (
-  `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-  `result_id` CHAR(255) NOT NULL DEFAULT '' COMMENT 'The unique identifier for the result that was chosen',
-  `user_id` bigint NULL COMMENT 'The user that chose the result',
-  `location` CHAR(255) NULL DEFAULT NULL COMMENT 'Sender location, only for bots that require user location',
-  `inline_message_id` CHAR(255) NULL DEFAULT NULL COMMENT 'Identifier of the sent inline message',
-  `query` TEXT NOT NULL COMMENT 'The query that was used to obtain the result',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `message` (
-  `chat_id` bigint COMMENT 'Unique chat identifier',
-  `sender_chat_id` bigint COMMENT 'Sender of the message, sent on behalf of a chat',
-  `id` bigint UNSIGNED COMMENT 'Unique message identifier',
-  `message_thread_id` bigint(20) DEFAULT NULL COMMENT 'Unique identifier of a message thread to which the message belongs; for supergroups only',
-  `user_id` bigint NULL COMMENT 'Unique user identifier',
-  `date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was sent in timestamp format',
-  `forward_from` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier, sender of the original message',
-  `forward_from_chat` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier, chat the original message belongs to',
-  `forward_from_message_id` bigint NULL DEFAULT NULL COMMENT 'Unique chat identifier of the original message in the channel',
-  `forward_signature` TEXT NULL DEFAULT NULL COMMENT 'For messages forwarded from channels, signature of the post author if present',
-  `forward_sender_name` TEXT NULL DEFAULT NULL COMMENT 'Sender''s name for messages forwarded from users who disallow adding a link to their account in forwarded messages',
-  `forward_date` timestamp NULL DEFAULT NULL COMMENT 'date the original message was sent in timestamp format',
-  `is_topic_message` tinyint(1) DEFAULT 0 COMMENT 'True, if the message is sent to a forum topic',
-  `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',
-  `author_signature` TEXT COMMENT 'Signature of the post author for messages in channels',
-  `text` TEXT COMMENT 'For text messages, the actual UTF-8 text of the message max message length 4096 char utf8mb4',
-  `entities` TEXT COMMENT 'For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text',
-  `caption_entities` TEXT COMMENT 'For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption',
-  `audio` TEXT COMMENT 'Audio object. Message is an audio file, information about the file',
-  `document` TEXT COMMENT 'Document object. Message is a general file, information about the file',
-  `animation` TEXT COMMENT 'Message is an animation, information about the animation',
-  `game` TEXT COMMENT 'Game object. Message is a game, information about the game',
-  `photo` TEXT COMMENT 'Array of PhotoSize objects. Message is a photo, available sizes of the photo',
-  `sticker` TEXT COMMENT 'Sticker object. Message is a sticker, information about the sticker',
-  `story` TEXT COMMENT 'Story object. Message is a forwarded story',
-  `video` TEXT COMMENT 'Video object. Message is a video, information about the video',
-  `voice` TEXT COMMENT 'Voice Object. Message is a Voice, information about the Voice',
-  `video_note` TEXT COMMENT 'VoiceNote Object. Message is a Video Note, information about the Video Note',
-  `caption` TEXT COMMENT  'For message with caption, the actual UTF-8 text of the caption',
-  `has_media_spoiler` tinyint(1) DEFAULT 0 COMMENT 'True, if the message media is covered by a spoiler animation',
-  `contact` TEXT COMMENT 'Contact object. Message is a shared contact, information about the contact',
-  `location` TEXT COMMENT 'Location object. Message is a shared location, information about the location',
-  `venue` TEXT COMMENT 'Venue object. Message is a Venue, information about the Venue',
-  `poll` TEXT COMMENT 'Poll object. Message is a native poll, information about the poll',
-  `dice` TEXT COMMENT 'Message is a dice with random value from 1 to 6',
-  `new_chat_members` TEXT COMMENT 'List of unique user identifiers, new member(s) were added to the group, information about them (one of these members may be the bot itself)',
-  `left_chat_member` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier, a member was removed from the group, information about them (this member may be the bot itself)',
-  `new_chat_title` CHAR(255) DEFAULT NULL COMMENT 'A chat title was changed to this value',
-  `new_chat_photo` TEXT COMMENT 'Array of PhotoSize objects. A chat photo was change to this value',
-  `delete_chat_photo` tinyint(1) DEFAULT 0 COMMENT 'Informs that the chat photo was deleted',
-  `group_chat_created` tinyint(1) DEFAULT 0 COMMENT 'Informs that the group has been created',
-  `supergroup_chat_created` tinyint(1) DEFAULT 0 COMMENT 'Informs that the supergroup has been created',
-  `channel_chat_created` tinyint(1) DEFAULT 0 COMMENT 'Informs that the channel chat has been created',
-  `message_auto_delete_timer_changed` TEXT COMMENT 'MessageAutoDeleteTimerChanged object. Message is a service message: auto-delete timer settings changed in the chat',
-  `migrate_to_chat_id` bigint NULL DEFAULT NULL COMMENT 'Migrate to chat identifier. The group has been migrated to a supergroup with the specified identifier',
-  `migrate_from_chat_id` bigint NULL DEFAULT NULL COMMENT 'Migrate from chat identifier. The supergroup has been migrated from a group with the specified identifier',
-  `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',
-  `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',
-  `passport_data` TEXT NULL COMMENT 'Telegram Passport data',
-  `proximity_alert_triggered` TEXT NULL COMMENT 'Service message. A user in the chat triggered another user''s proximity alert while sharing Live Location.',
-  `forum_topic_created` TEXT DEFAULT NULL COMMENT 'Service message: forum topic created',
-  `forum_topic_edited` TEXT DEFAULT NULL COMMENT 'Service message: forum topic edited',
-  `forum_topic_closed` TEXT DEFAULT NULL COMMENT 'Service message: forum topic closed',
-  `forum_topic_reopened` TEXT DEFAULT NULL COMMENT 'Service message: forum topic reopened',
-  `general_forum_topic_hidden` TEXT DEFAULT NULL COMMENT 'Service message: the General forum topic hidden',
-  `general_forum_topic_unhidden` TEXT DEFAULT NULL COMMENT 'Service message: the General forum topic unhidden',
-  `video_chat_scheduled` TEXT COMMENT 'Service message: video chat scheduled',
-  `video_chat_started` TEXT COMMENT 'Service message: video chat started',
-  `video_chat_ended` TEXT COMMENT 'Service message: video chat ended',
-  `video_chat_participants_invited` TEXT COMMENT 'Service message: new participants invited to a video chat',
-  `web_app_data` TEXT COMMENT 'Service message: data sent by a Web App',
-  `reply_markup` TEXT NULL COMMENT 'Inline keyboard attached to the message',
-
-  PRIMARY KEY (`chat_id`, `id`),
-  KEY `user_id` (`user_id`),
-  KEY `forward_from` (`forward_from`),
-  KEY `forward_from_chat` (`forward_from_chat`),
-  KEY `reply_to_chat` (`reply_to_chat`),
-  KEY `reply_to_message` (`reply_to_message`),
-  KEY `via_bot` (`via_bot`),
-  KEY `left_chat_member` (`left_chat_member`),
-  KEY `migrate_from_chat_id` (`migrate_from_chat_id`),
-  KEY `migrate_to_chat_id` (`migrate_to_chat_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
-  FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
-  FOREIGN KEY (`forward_from`) REFERENCES `user` (`id`),
-  FOREIGN KEY (`forward_from_chat`) REFERENCES `chat` (`id`),
-  FOREIGN KEY (`reply_to_chat`, `reply_to_message`) REFERENCES `message` (`chat_id`, `id`),
-  FOREIGN KEY (`via_bot`) REFERENCES `user` (`id`),
-  FOREIGN KEY (`left_chat_member`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `edited_message` (
-  `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-  `chat_id` bigint COMMENT 'Unique chat identifier',
-  `message_id` bigint UNSIGNED COMMENT 'Unique message identifier',
-  `user_id` bigint NULL COMMENT 'Unique user identifier',
-  `edit_date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was edited in timestamp format',
-  `text` TEXT COMMENT 'For text messages, the actual UTF-8 text of the message max message length 4096 char utf8',
-  `entities` TEXT COMMENT 'For text messages, special entities like usernames, URLs, bot commands, etc. that appear in the text',
-  `caption` TEXT COMMENT  'For message with caption, the actual UTF-8 text of the caption',
-
-  PRIMARY KEY (`id`),
-  KEY `chat_id` (`chat_id`),
-  KEY `message_id` (`message_id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
-  FOREIGN KEY (`chat_id`, `message_id`) REFERENCES `message` (`chat_id`, `id`),
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `callback_query` (
-  `id` bigint UNSIGNED COMMENT 'Unique identifier for this query',
-  `user_id` bigint NULL COMMENT 'Unique user identifier',
-  `chat_id` bigint NULL COMMENT 'Unique chat identifier',
-  `message_id` bigint UNSIGNED COMMENT 'Unique message identifier',
-  `inline_message_id` CHAR(255) NULL DEFAULT NULL COMMENT 'Identifier of the message sent via the bot in inline mode, that originated the query',
-  `chat_instance` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent',
-  `data` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Data associated with the callback button',
-  `game_short_name` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Short name of a Game to be returned, serves as the unique identifier for the game',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-  KEY `chat_id` (`chat_id`),
-  KEY `message_id` (`message_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
-  FOREIGN KEY (`chat_id`, `message_id`) REFERENCES `message` (`chat_id`, `id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `shipping_query` (
-  `id` bigint UNSIGNED COMMENT 'Unique query identifier',
-  `user_id` bigint COMMENT 'User who sent the query',
-  `invoice_payload` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Bot specified invoice payload',
-  `shipping_address` CHAR(255) NOT NULL DEFAULT '' COMMENT 'User specified shipping address',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `pre_checkout_query` (
-  `id` bigint UNSIGNED COMMENT 'Unique query identifier',
-  `user_id` bigint COMMENT 'User who sent the query',
-  `currency` CHAR(3) COMMENT 'Three-letter ISO 4217 currency code',
-  `total_amount` bigint COMMENT 'Total price in the smallest units of the currency',
-  `invoice_payload` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Bot specified invoice payload',
-  `shipping_option_id` CHAR(255) NULL COMMENT 'Identifier of the shipping option chosen by the user',
-  `order_info` TEXT NULL COMMENT 'Order info provided by the user',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `poll` (
-  `id` bigint UNSIGNED COMMENT 'Unique poll identifier',
-  `question` text NOT NULL COMMENT 'Poll question',
-  `options` text NOT NULL COMMENT 'List of poll options',
-  `total_voter_count` int UNSIGNED COMMENT 'Total number of users that voted in the poll',
-  `is_closed` tinyint(1) DEFAULT 0 COMMENT 'True, if the poll is closed',
-  `is_anonymous` tinyint(1) DEFAULT 1 COMMENT 'True, if the poll is anonymous',
-  `type` char(255) COMMENT 'Poll type, currently can be “regular” or “quiz”',
-  `allows_multiple_answers` tinyint(1) DEFAULT 0 COMMENT 'True, if the poll allows multiple answers',
-  `correct_option_id` int UNSIGNED COMMENT '0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.',
-  `explanation` varchar(255) DEFAULT NULL COMMENT 'Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters',
-  `explanation_entities` text DEFAULT NULL COMMENT 'Special entities like usernames, URLs, bot commands, etc. that appear in the explanation',
-  `open_period` int UNSIGNED DEFAULT NULL COMMENT 'Amount of time in seconds the poll will be active after creation',
-  `close_date` timestamp NULL DEFAULT NULL COMMENT 'Point in time (Unix timestamp) when the poll will be automatically closed',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `poll_answer` (
-  `poll_id` bigint UNSIGNED COMMENT 'Unique poll identifier',
-  `user_id` bigint NOT NULL COMMENT 'The user, who changed the answer to the poll',
-  `option_ids` text NOT NULL COMMENT '0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote.',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`poll_id`, `user_id`),
-  FOREIGN KEY (`poll_id`) REFERENCES `poll` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `chat_member_updated` (
-  `id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-  `chat_id` BIGINT NOT NULL COMMENT 'Chat the user belongs to',
-  `user_id` BIGINT NOT NULL COMMENT 'Performer of the action, which resulted in the change',
-  `date` TIMESTAMP NOT NULL COMMENT 'Date the change was done in Unix time',
-  `old_chat_member` TEXT NOT NULL COMMENT 'Previous information about the chat member',
-  `new_chat_member` TEXT NOT NULL COMMENT 'New information about the chat member',
-  `invite_link` TEXT NULL COMMENT 'Chat invite link, which was used by the user to join the chat; for joining by invite link events only',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `chat_join_request` (
-  `id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-  `chat_id` BIGINT NOT NULL COMMENT 'Chat to which the request was sent',
-  `user_id` BIGINT NOT NULL COMMENT 'User that sent the join request',
-  `date` TIMESTAMP NOT NULL COMMENT 'Date the request was sent in Unix time',
-  `bio` TEXT NULL COMMENT 'Optional. Bio of the user',
-  `invite_link` TEXT NULL COMMENT 'Optional. Chat invite link that was used by the user to send the join request',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-
-  FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
-  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',
-  `message_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming message of any kind - text, photo, sticker, etc.',
-  `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',
-  `shipping_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming shipping query. Only for invoices with flexible price',
-  `pre_checkout_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming pre-checkout query. Contains full information about checkout',
-  `poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New poll state. Bots receive only updates about polls, which are sent or stopped by the bot',
-  `poll_answer_poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.',
-  `my_chat_member_updated_id` BIGINT UNSIGNED NULL COMMENT '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.',
-  `chat_member_updated_id` BIGINT UNSIGNED NULL COMMENT '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.',
-  `chat_join_request_id` BIGINT UNSIGNED NULL COMMENT 'A request to join the chat has been sent',
-
-  PRIMARY KEY (`id`),
-  KEY `message_id` (`message_id`),
-  KEY `chat_message_id` (`chat_id`, `message_id`),
-  KEY `edited_message_id` (`edited_message_id`),
-  KEY `channel_post_id` (`channel_post_id`),
-  KEY `edited_channel_post_id` (`edited_channel_post_id`),
-  KEY `inline_query_id` (`inline_query_id`),
-  KEY `chosen_inline_result_id` (`chosen_inline_result_id`),
-  KEY `callback_query_id` (`callback_query_id`),
-  KEY `shipping_query_id` (`shipping_query_id`),
-  KEY `pre_checkout_query_id` (`pre_checkout_query_id`),
-  KEY `poll_id` (`poll_id`),
-  KEY `poll_answer_poll_id` (`poll_answer_poll_id`),
-  KEY `my_chat_member_updated_id` (`my_chat_member_updated_id`),
-  KEY `chat_member_updated_id` (`chat_member_updated_id`),
-  KEY `chat_join_request_id` (`chat_join_request_id`),
-
-  FOREIGN KEY (`chat_id`, `message_id`) REFERENCES `message` (`chat_id`, `id`),
-  FOREIGN KEY (`edited_message_id`) REFERENCES `edited_message` (`id`),
-  FOREIGN KEY (`chat_id`, `channel_post_id`) REFERENCES `message` (`chat_id`, `id`),
-  FOREIGN KEY (`edited_channel_post_id`) REFERENCES `edited_message` (`id`),
-  FOREIGN KEY (`inline_query_id`) REFERENCES `inline_query` (`id`),
-  FOREIGN KEY (`chosen_inline_result_id`) REFERENCES `chosen_inline_result` (`id`),
-  FOREIGN KEY (`callback_query_id`) REFERENCES `callback_query` (`id`),
-  FOREIGN KEY (`shipping_query_id`) REFERENCES `shipping_query` (`id`),
-  FOREIGN KEY (`pre_checkout_query_id`) REFERENCES `pre_checkout_query` (`id`),
-  FOREIGN KEY (`poll_id`) REFERENCES `poll` (`id`),
-  FOREIGN KEY (`poll_answer_poll_id`) REFERENCES `poll_answer` (`poll_id`),
-  FOREIGN KEY (`my_chat_member_updated_id`) REFERENCES `chat_member_updated` (`id`),
-  FOREIGN KEY (`chat_member_updated_id`) REFERENCES `chat_member_updated` (`id`),
-  FOREIGN KEY (`chat_join_request_id`) REFERENCES `chat_join_request` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `conversation` (
-  `id` bigint(20) unsigned AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-  `user_id` bigint NULL DEFAULT NULL COMMENT 'Unique user identifier',
-  `chat_id` bigint NULL DEFAULT NULL COMMENT 'Unique user or chat identifier',
-  `status` ENUM('active', 'cancelled', 'stopped') NOT NULL DEFAULT 'active' COMMENT 'Conversation state',
-  `command` varchar(160) DEFAULT '' COMMENT 'Default command to execute',
-  `notes` text DEFAULT NULL COMMENT 'Data stored from command',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-  `updated_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date update',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-  KEY `chat_id` (`chat_id`),
-  KEY `status` (`status`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
-  FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `request_limiter` (
-  `id` bigint UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-  `chat_id` char(255) NULL DEFAULT NULL COMMENT 'Unique chat identifier',
-  `inline_message_id` char(255) NULL DEFAULT NULL COMMENT 'Identifier of the sent inline message',
-  `method` char(255) DEFAULT NULL COMMENT 'Request method',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
diff --git a/tests/Unit/Commands/CommandTest.php b/tests/Unit/Commands/CommandTest.php
deleted file mode 100644
index 35be1d797..000000000
--- a/tests/Unit/Commands/CommandTest.php
+++ /dev/null
@@ -1,171 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Commands;
-
-use Longman\TelegramBot\Commands\Command;
-use Longman\TelegramBot\Telegram;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-use Longman\TelegramBot\Tests\Unit\TestHelpers;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class CommandTest extends TestCase
-{
-    /**
-     * @var string
-     */
-    private $command_namespace = Command::class;
-
-    /**
-     * @var Telegram
-     */
-    private $telegram;
-
-    /**
-     * @var Command
-     */
-    private $command_stub;
-
-    /**
-     * @var Telegram
-     */
-    private $telegram_with_config;
-
-    /**
-     * @var Command
-     */
-    private $command_stub_with_config;
-
-    public function setUp(): void
-    {
-        //Default command object
-        $this->telegram     = new Telegram(self::$dummy_api_key, 'testbot');
-        $this->command_stub = $this->getMockForAbstractClass($this->command_namespace, [$this->telegram]);
-
-        //Create separate command object that contain a command config
-        $this->telegram_with_config = new Telegram(self::$dummy_api_key, 'testbot');
-        $this->telegram_with_config->setCommandConfig('command_name', ['config_key' => 'config_value']);
-        $this->command_stub_with_config = $this->getMockBuilder($this->command_namespace)
-            ->disableOriginalConstructor()
-            ->getMockForAbstractClass();
-        //Set a name for the object property so that the constructor can set the config correctly
-        TestHelpers::setObjectProperty($this->command_stub_with_config, 'name', 'command_name');
-        $this->command_stub_with_config->__construct($this->telegram_with_config);
-    }
-
-    // Test idea from here: http://stackoverflow.com/a/4371606
-    public function testCommandConstructorNeedsTelegramObject(): void
-    {
-        $exception_count = 0;
-        $params_to_test  = [
-            [],
-            [null],
-            [12345],
-            ['something'],
-            [new \stdClass()],
-            [$this->telegram], // only this one is valid
-        ];
-
-        foreach ($params_to_test as $param) {
-            try {
-                $this->getMockForAbstractClass($this->command_namespace, $param);
-            } catch (\Throwable $e) {
-                $exception_count++;
-            }
-        }
-
-        self::assertEquals(5, $exception_count);
-    }
-
-    public function testCommandHasCorrectTelegramObject(): void
-    {
-        self::assertSame($this->telegram, $this->command_stub->getTelegram());
-    }
-
-    public function testDefaultCommandName(): void
-    {
-        self::assertEmpty($this->command_stub->getName());
-    }
-
-    public function testDefaultCommandDescription(): void
-    {
-        self::assertEquals('Command description', $this->command_stub->getDescription());
-    }
-
-    public function testDefaultCommandUsage(): void
-    {
-        self::assertEquals('Command usage', $this->command_stub->getUsage());
-    }
-
-    public function testDefaultCommandVersion(): void
-    {
-        self::assertEquals('1.0.0', $this->command_stub->getVersion());
-    }
-
-    public function testDefaultCommandIsEnabled(): void
-    {
-        self::assertTrue($this->command_stub->isEnabled());
-    }
-
-    public function testDefaultCommandShownInHelp(): void
-    {
-        self::assertTrue($this->command_stub->showInHelp());
-    }
-
-    public function testDefaultCommandNeedsMysql(): void
-    {
-        self::markTestSkipped('Think about better test');
-    }
-
-    public function testDefaultCommandEmptyConfig(): void
-    {
-        self::assertSame([], $this->command_stub->getConfig());
-    }
-
-    public function testDefaultCommandUpdateNull(): void
-    {
-        self::assertNull($this->command_stub->getUpdate());
-    }
-
-    public function testCommandSetUpdateAndMessage(): void
-    {
-        $stub = $this->command_stub;
-
-        self::assertEquals(null, $stub->getUpdate());
-        self::assertEquals(null, $stub->getMessage());
-
-        $update  = TestHelpers::getFakeUpdateObject();
-        $message = $update->getMessage();
-        $stub->setUpdate($update);
-        self::assertEquals($update, $stub->getUpdate());
-        self::assertEquals($message, $stub->getMessage());
-    }
-
-    public function testCommandWithConfigNotEmptyConfig(): void
-    {
-        self::assertNotEmpty($this->command_stub_with_config->getConfig());
-    }
-
-    public function testCommandWithConfigCorrectConfig(): void
-    {
-        self::assertEquals(['config_key' => 'config_value'], $this->command_stub_with_config->getConfig());
-        self::assertEquals(['config_key' => 'config_value'], $this->command_stub_with_config->getConfig(null));
-        self::assertEquals(['config_key' => 'config_value'], $this->command_stub_with_config->getConfig());
-        self::assertEquals('config_value', $this->command_stub_with_config->getConfig('config_key'));
-        self::assertEquals(null, $this->command_stub_with_config->getConfig('not_config_key'));
-    }
-}
diff --git a/tests/Unit/Commands/CommandTestCase.php b/tests/Unit/Commands/CommandTestCase.php
deleted file mode 100644
index 782347d31..000000000
--- a/tests/Unit/Commands/CommandTestCase.php
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Commands;
-
-use Longman\TelegramBot\Commands\Command;
-use Longman\TelegramBot\Telegram;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @package         TelegramTest
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @link            https://github.com/php-telegram-bot/core
- */
-class CommandTestCase extends TestCase
-{
-    /**
-     * @var Telegram
-     */
-    protected $telegram;
-
-    /**
-     * @var Command
-     */
-    protected $command;
-
-    /**
-     * setUp
-     */
-    public function setUp(): void
-    {
-        $this->telegram = new Telegram(self::$dummy_api_key, 'testbot');
-
-        // Add custom commands dedicated to do some tests.
-        $this->telegram->addCommandsPath(__DIR__ . '/CustomTestCommands');
-        $this->telegram->getCommandsList();
-    }
-
-    /**
-     * Make sure the version number is in the format x.x.x, x.x or x
-     */
-    public function testVersionNumberFormat(): void
-    {
-        self::assertRegExp('/^(\d+\\.)?(\d+\\.)?(\d+)$/', $this->command->getVersion());
-    }
-}
diff --git a/tests/Unit/Commands/CustomTestCommands/DummyAdminCommand.php b/tests/Unit/Commands/CustomTestCommands/DummyAdminCommand.php
deleted file mode 100644
index b81114d2a..000000000
--- a/tests/Unit/Commands/CustomTestCommands/DummyAdminCommand.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Dummy\AdminCommands;
-
-use Longman\TelegramBot\Commands\AdminCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Request;
-
-/**
- * Test "/dummy_admin" command
- */
-class DummyAdminCommand extends AdminCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'dummy_admin';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Dummy AdminCommand';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/dummy_admin';
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     */
-    public function execute(): ServerResponse
-    {
-        return Request::emptyResponse();
-    }
-}
diff --git a/tests/Unit/Commands/CustomTestCommands/DummySystemCommand.php b/tests/Unit/Commands/CustomTestCommands/DummySystemCommand.php
deleted file mode 100644
index d9ed58597..000000000
--- a/tests/Unit/Commands/CustomTestCommands/DummySystemCommand.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Dummy\SystemCommands;
-
-use Longman\TelegramBot\Commands\SystemCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Request;
-
-/**
- * Test "/dummy_system" command
- */
-class DummySystemCommand extends SystemCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'dummy_system';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Dummy SystemCommand';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/dummy_system';
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     */
-    public function execute(): ServerResponse
-    {
-        return Request::emptyResponse();
-    }
-}
diff --git a/tests/Unit/Commands/CustomTestCommands/DummyUserCommand.php b/tests/Unit/Commands/CustomTestCommands/DummyUserCommand.php
deleted file mode 100644
index 7a20b1ba8..000000000
--- a/tests/Unit/Commands/CustomTestCommands/DummyUserCommand.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Dummy\UserCommands;
-
-use Longman\TelegramBot\Commands\UserCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Request;
-
-/**
- * Test "/dummy_user" command
- */
-class DummyUserCommand extends UserCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'dummy_user';
-
-    /**
-     * @var string
-     */
-    protected $description = 'Dummy UserCommand';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/dummy_user';
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     */
-    public function execute(): ServerResponse
-    {
-        return Request::emptyResponse();
-    }
-}
diff --git a/tests/Unit/Commands/CustomTestCommands/HiddenCommand.php b/tests/Unit/Commands/CustomTestCommands/HiddenCommand.php
deleted file mode 100644
index 46f11dfc8..000000000
--- a/tests/Unit/Commands/CustomTestCommands/HiddenCommand.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\UserCommands;
-
-use Longman\TelegramBot\Commands\UserCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Request;
-
-/**
- * Test "/hidden" command to test $show_in_help
- */
-class HiddenCommand extends UserCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'hidden';
-
-    /**
-     * @var string
-     */
-    protected $description = 'This command is hidden in help';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/hidden';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.0.0';
-
-    /**
-     * @var bool
-     */
-    protected $show_in_help = false;
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     */
-    public function execute(): ServerResponse
-    {
-        return Request::emptyResponse();
-    }
-}
diff --git a/tests/Unit/Commands/CustomTestCommands/VisibleCommand.php b/tests/Unit/Commands/CustomTestCommands/VisibleCommand.php
deleted file mode 100644
index 3900c616d..000000000
--- a/tests/Unit/Commands/CustomTestCommands/VisibleCommand.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Commands\UserCommands;
-
-use Longman\TelegramBot\Commands\UserCommand;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Request;
-
-/**
- * Test "/visible" command to test $show_in_help
- */
-class VisibleCommand extends UserCommand
-{
-    /**
-     * @var string
-     */
-    protected $name = 'visible';
-
-    /**
-     * @var string
-     */
-    protected $description = 'This command is visible in help';
-
-    /**
-     * @var string
-     */
-    protected $usage = '/visible';
-
-    /**
-     * @var string
-     */
-    protected $version = '1.0.0';
-
-    /**
-     * @var bool
-     */
-    protected $show_in_help = true;
-
-    /**
-     * Command execute method
-     *
-     * @return mixed
-     */
-    public function execute(): ServerResponse
-    {
-        return Request::emptyResponse();
-    }
-}
diff --git a/tests/Unit/ConversationTest.php b/tests/Unit/ConversationTest.php
deleted file mode 100644
index f5205ffae..000000000
--- a/tests/Unit/ConversationTest.php
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit;
-
-use Longman\TelegramBot\Conversation;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Telegram;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class ConversationTest extends TestCase
-{
-
-    protected function setUp(): void
-    {
-        $credentials = [
-            'host'     => PHPUNIT_DB_HOST,
-            'port'     => PHPUNIT_DB_PORT,
-            'database' => PHPUNIT_DB_NAME,
-            'user'     => PHPUNIT_DB_USER,
-            'password' => PHPUNIT_DB_PASS,
-        ];
-
-        $telegram = new Telegram(self::$dummy_api_key, 'testbot');
-        $telegram->enableMySql($credentials);
-
-        //Make sure we start with an empty DB for each test.
-        TestHelpers::emptyDb($credentials);
-    }
-
-    public function testConversationThatDoesntExistPropertiesSetCorrectly(): void
-    {
-        $conversation = new Conversation(123, 456);
-        self::assertSame(123, $conversation->getUserId());
-        self::assertSame(456, $conversation->getChatId());
-        self::assertEmpty($conversation->getCommand());
-    }
-
-    public function testConversationThatExistsPropertiesSetCorrectly(): void
-    {
-        $info         = TestHelpers::startFakeConversation();
-        $conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
-        self::assertSame($info['user_id'], $conversation->getUserId());
-        self::assertSame($info['chat_id'], $conversation->getChatId());
-        self::assertSame('command', $conversation->getCommand());
-    }
-
-    public function testConversationThatDoesntExistWithoutCommand(): void
-    {
-        $conversation = new Conversation(1, 1);
-        self::assertFalse($conversation->exists());
-        self::assertEmpty($conversation->getCommand());
-    }
-
-    public function testConversationThatDoesntExistWithCommand(): void
-    {
-        $this->expectException(TelegramException::class);
-        new Conversation(1, 1, 'command');
-    }
-
-    public function testNewConversationThatWontExistWithoutCommand(): void
-    {
-        TestHelpers::startFakeConversation();
-        $conversation = new Conversation(0, 0);
-        self::assertFalse($conversation->exists());
-        self::assertEmpty($conversation->getCommand());
-    }
-
-    public function testNewConversationThatWillExistWithCommand(): void
-    {
-        $info         = TestHelpers::startFakeConversation();
-        $conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
-        self::assertTrue($conversation->exists());
-        self::assertEquals('command', $conversation->getCommand());
-    }
-
-    public function testStopConversation(): void
-    {
-        $info         = TestHelpers::startFakeConversation();
-        $conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
-        self::assertTrue($conversation->exists());
-        $conversation->stop();
-
-        $conversation2 = new Conversation($info['user_id'], $info['chat_id']);
-        self::assertFalse($conversation2->exists());
-    }
-
-    public function testCancelConversation(): void
-    {
-        $info         = TestHelpers::startFakeConversation();
-        $conversation = new Conversation($info['user_id'], $info['chat_id'], 'command');
-        self::assertTrue($conversation->exists());
-        $conversation->cancel();
-
-        $conversation2 = new Conversation($info['user_id'], $info['chat_id']);
-        self::assertFalse($conversation2->exists());
-    }
-
-    public function testUpdateConversationNotes(): void
-    {
-        $info                = TestHelpers::startFakeConversation();
-        $conversation        = new Conversation($info['user_id'], $info['chat_id'], 'command');
-        $conversation->notes = 'newnote';
-        $conversation->update();
-
-        $conversation2 = new Conversation($info['user_id'], $info['chat_id'], 'command');
-        self::assertSame('newnote', $conversation2->notes);
-
-        $conversation3 = new Conversation($info['user_id'], $info['chat_id']);
-        self::assertSame('newnote', $conversation3->notes);
-    }
-}
diff --git a/tests/Unit/Entities/AudioTest.php b/tests/Unit/Entities/AudioTest.php
deleted file mode 100644
index ce9eb64b7..000000000
--- a/tests/Unit/Entities/AudioTest.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Audio;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-use Longman\TelegramBot\Tests\Unit\TestHelpers;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Baev Nikolay <gametester3d@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class AudioTest extends TestCase
-{
-    /**
-     * @var array
-     */
-    private $record;
-
-    public function setUp(): void
-    {
-        $this->record = TestHelpers::getFakeRecordedAudio();
-    }
-
-    public function testInstance(): void
-    {
-        $audio = new Audio($this->record);
-        self::assertInstanceOf(Audio::class, $audio);
-    }
-
-    public function testGetProperties(): void
-    {
-        $audio = new Audio($this->record);
-        self::assertEquals($this->record['file_id'], $audio->getFileId());
-        self::assertEquals($this->record['duration'], $audio->getDuration());
-        self::assertEquals($this->record['performer'], $audio->getPerformer());
-        self::assertEquals($this->record['title'], $audio->getTitle());
-        self::assertEquals($this->record['mime_type'], $audio->getMimeType());
-        self::assertEquals($this->record['file_size'], $audio->getFileSize());
-    }
-}
diff --git a/tests/Unit/Entities/ChatTest.php b/tests/Unit/Entities/ChatTest.php
deleted file mode 100644
index 0f2c03aa6..000000000
--- a/tests/Unit/Entities/ChatTest.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Tests\Unit\TestCase;
-use Longman\TelegramBot\Tests\Unit\TestHelpers;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class ChatTest extends TestCase
-{
-    public function testChatType(): void
-    {
-        $chat = TestHelpers::getFakeChatObject();
-        self::assertEquals('private', $chat->getType());
-
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => null]);
-        self::assertEquals('group', $chat->getType());
-
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => 'supergroup']);
-        self::assertEquals('supergroup', $chat->getType());
-
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => 'channel']);
-        self::assertEquals('channel', $chat->getType());
-    }
-
-    public function testIsChatType(): void
-    {
-        $chat = TestHelpers::getFakeChatObject();
-        self::assertTrue($chat->isPrivateChat());
-
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => null]);
-        self::assertTrue($chat->isGroupChat());
-
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => 'supergroup']);
-        self::assertTrue($chat->isSuperGroup());
-
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => 'channel']);
-        self::assertTrue($chat->isChannel());
-    }
-
-    public function testTryMention(): void
-    {
-        // Username.
-        $chat = TestHelpers::getFakeChatObject(['id' => 1, 'first_name' => 'John', 'last_name' => 'Taylor', 'username' => 'jtaylor']);
-        self::assertEquals('@jtaylor', $chat->tryMention());
-
-        // First name.
-        $chat = TestHelpers::getFakeChatObject(['id' => 1, 'first_name' => 'John', 'last_name' => null, 'username' => null]);
-        self::assertEquals('John', $chat->tryMention());
-
-        // First and Last name.
-        $chat = TestHelpers::getFakeChatObject(['id' => 1, 'first_name' => 'John', 'last_name' => 'Taylor', 'username' => null]);
-        self::assertEquals('John Taylor', $chat->tryMention());
-
-        // Non-private chat should return title.
-        $chat = TestHelpers::getFakeChatObject(['id' => -123, 'type' => null, 'title' => 'My group chat']);
-        self::assertSame('My group chat', $chat->tryMention());
-    }
-}
diff --git a/tests/Unit/Entities/EntityTest.php b/tests/Unit/Entities/EntityTest.php
deleted file mode 100644
index 47299c878..000000000
--- a/tests/Unit/Entities/EntityTest.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Entity;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Baev Nikolay <gametester3d@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class EntityTest extends TestCase
-{
-    public function testEscapeMarkdown(): void
-    {
-        // Make sure all characters that need escaping are escaped.
-
-        // Markdown V1
-        self::assertEquals('\[\`\*\_', Entity::escapeMarkdown('[`*_'));
-        self::assertEquals('\*mark\*\_down\_~test~', Entity::escapeMarkdown('*mark*_down_~test~'));
-
-        // Markdown V2
-        self::assertEquals('\_\*\[\]\(\)\~\`\>\#\+\-\=\|\{\}\.\!', Entity::escapeMarkdownV2('_*[]()~`>#+-=|{}.!'));
-        self::assertEquals('\*mark\*\_down\_\~test\~', Entity::escapeMarkdownV2('*mark*_down_~test~'));
-    }
-
-    public function testSettingDynamicParameterWorks(): void
-    {
-        $entity = new class ( [] ) extends Entity { }; // phpcs:ignore
-
-        $entity->newParameter = 'test';
-
-        $this->assertEquals('test', $entity->newParameter);
-    }
-
-    public function testGettingUnknownDynamicParameterReturnsNull(): void
-    {
-        $entity = new class ( [] ) extends Entity { }; // phpcs:ignore
-
-        $this->assertNull($entity->unknownParameter);
-    }
-}
diff --git a/tests/Unit/Entities/FileTest.php b/tests/Unit/Entities/FileTest.php
deleted file mode 100644
index 822f4fac5..000000000
--- a/tests/Unit/Entities/FileTest.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\File;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Baev Nikolay <gametester3d@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class FileTest extends TestCase
-{
-    /**
-     * @var array
-     */
-    private $data;
-
-    public function setUp(): void
-    {
-        $this->data = [
-            'file_id'   => (int) mt_rand(1, 99),
-            'file_size' => (int) mt_rand(100, 99999),
-            'file_path' => 'home' . DIRECTORY_SEPARATOR . 'phpunit',
-        ];
-    }
-
-    public function testBaseStageLocation(): void
-    {
-        $file = new File($this->data);
-        self::assertInstanceOf(File::class, $file);
-    }
-
-    public function testGetFileId(): void
-    {
-        $file = new File($this->data);
-        $id   = $file->getFileId();
-        self::assertIsInt($id);
-        self::assertEquals($this->data['file_id'], $id);
-    }
-
-    public function testGetFileSize(): void
-    {
-        $file = new File($this->data);
-        $size = $file->getFileSize();
-        self::assertIsInt($size);
-        self::assertEquals($this->data['file_size'], $size);
-    }
-
-    public function testGetFilePath(): void
-    {
-        $file = new File($this->data);
-        $path = $file->getFilePath();
-        self::assertEquals($this->data['file_path'], $path);
-    }
-
-    public function testGetFileSizeWithoutData(): void
-    {
-        unset($this->data['file_size']);
-        $file = new File($this->data);
-        $id   = $file->getFileSize();
-        self::assertNull($id);
-    }
-
-    public function testGetFilePathWithoutData(): void
-    {
-        unset($this->data['file_path']);
-        $file = new File($this->data);
-        $path = $file->getFilePath();
-        self::assertNull($path);
-    }
-}
diff --git a/tests/Unit/Entities/InlineKeyboardButtonTest.php b/tests/Unit/Entities/InlineKeyboardButtonTest.php
deleted file mode 100644
index 384430aa5..000000000
--- a/tests/Unit/Entities/InlineKeyboardButtonTest.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Games\CallbackGame;
-use Longman\TelegramBot\Entities\InlineKeyboardButton;
-use Longman\TelegramBot\Entities\SwitchInlineQueryChosenChat;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class InlineKeyboardButtonTest extends TestCase
-{
-    public function testInlineKeyboardButtonSuccess(): void
-    {
-        new InlineKeyboardButton(['text' => 'message', 'url' => 'url_value']);
-        new InlineKeyboardButton(['text' => 'message', 'callback_data' => 'callback_data_value']);
-        new InlineKeyboardButton(['text' => 'message', 'switch_inline_query' => 'switch_inline_query_value']);
-        new InlineKeyboardButton(['text' => 'message', 'switch_inline_query' => '']); // Allow empty string.
-        new InlineKeyboardButton(['text' => 'message', 'switch_inline_query_current_chat' => 'switch_inline_query_current_chat_value']);
-        new InlineKeyboardButton(['text' => 'message', 'switch_inline_query_current_chat' => '']); // Allow empty string.
-        new InlineKeyboardButton(['text' => 'message', 'switch_inline_query_chosen_chat' => new SwitchInlineQueryChosenChat([])]); // Allow empty string.
-        new InlineKeyboardButton(['text' => 'message', 'callback_game' => new CallbackGame([])]);
-        new InlineKeyboardButton(['text' => 'message', 'pay' => true]);
-        self::assertTrue(true);
-    }
-
-    public function testInlineKeyboardButtonCouldBe(): void
-    {
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'url' => 'url_value']
-        ));
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'callback_data' => 'callback_data_value']
-        ));
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'switch_inline_query' => 'switch_inline_query_value']
-        ));
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'switch_inline_query_current_chat' => 'switch_inline_query_current_chat_value']
-        ));
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'switch_inline_query_chosen_chat' => new SwitchInlineQueryChosenChat([])]
-        ));
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'callback_game' => new CallbackGame([])]
-        ));
-        self::assertTrue(InlineKeyboardButton::couldBe(
-            ['text' => 'message', 'pay' => true]
-        ));
-
-        self::assertFalse(InlineKeyboardButton::couldBe(['no_text' => 'message']));
-        self::assertFalse(InlineKeyboardButton::couldBe(['text' => 'message']));
-        self::assertFalse(InlineKeyboardButton::couldBe(['url' => 'url_value']));
-        self::assertFalse(InlineKeyboardButton::couldBe([
-            'callback_data' => 'callback_data_value'
-        ]));
-        self::assertFalse(InlineKeyboardButton::couldBe([
-            'switch_inline_query' => 'switch_inline_query_value'
-        ]));
-        self::assertFalse(InlineKeyboardButton::couldBe([
-            'switch_inline_query_current_chat' => 'switch_inline_query_current_chat_value'
-        ]));
-        self::assertFalse(InlineKeyboardButton::couldBe([
-            'switch_inline_query_chosen_chat' => new SwitchInlineQueryChosenChat([])
-        ]));
-        self::assertFalse(InlineKeyboardButton::couldBe(['callback_game' => new CallbackGame([])]));
-        self::assertFalse(InlineKeyboardButton::couldBe(['pay' => true]));
-
-        self::assertFalse(InlineKeyboardButton::couldBe([
-            'url'                              => 'url_value',
-            'callback_data'                    => 'callback_data_value',
-            'switch_inline_query'              => 'switch_inline_query_value',
-            'switch_inline_query_current_chat' => 'switch_inline_query_current_chat_value',
-            'switch_inline_query_chosen_chat'  => new SwitchInlineQueryChosenChat([]),
-            'callback_game'                    => new CallbackGame([]),
-            'pay'                              => true,
-        ]));
-    }
-}
diff --git a/tests/Unit/Entities/InlineKeyboardTest.php b/tests/Unit/Entities/InlineKeyboardTest.php
deleted file mode 100644
index 9e30ced4e..000000000
--- a/tests/Unit/Entities/InlineKeyboardTest.php
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\InlineKeyboard;
-use Longman\TelegramBot\Entities\InlineKeyboardButton;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class InlineKeyboardTest extends TestCase
-{
-    private function getRandomButton($text): InlineKeyboardButton
-    {
-        $random_params = ['url', 'callback_data', 'switch_inline_query', 'switch_inline_query_current_chat', 'pay'];
-        $param         = $random_params[array_rand($random_params, 1)];
-        $data          = [
-            'text' => $text,
-            $param => 'random_param',
-        ];
-
-        return new InlineKeyboardButton($data);
-    }
-
-    public function testInlineKeyboardDataMalformedField(): void
-    {
-        $this->expectException(TelegramException::class);
-        $this->expectExceptionMessage('inline_keyboard field is not an array!');
-        new InlineKeyboard(['inline_keyboard' => 'wrong']);
-    }
-
-    public function testInlineKeyboardDataMalformedSubfield(): void
-    {
-        $this->expectException(TelegramException::class);
-        $this->expectExceptionMessage('inline_keyboard subfield is not an array!');
-        new InlineKeyboard(['inline_keyboard' => ['wrong']]);
-    }
-
-    public function testInlineKeyboardSingleButtonSingleRow(): void
-    {
-        $inline_keyboard = (new InlineKeyboard(
-            $this->getRandomButton('Button Text 1')
-        ))->getProperty('inline_keyboard');
-        self::assertSame('Button Text 1', $inline_keyboard[0][0]->getText());
-
-        $inline_keyboard = (new InlineKeyboard(
-            [$this->getRandomButton('Button Text 2')]
-        ))->getProperty('inline_keyboard');
-        self::assertSame('Button Text 2', $inline_keyboard[0][0]->getText());
-    }
-
-    public function testInlineKeyboardSingleButtonMultipleRows(): void
-    {
-        $keyboard = (new InlineKeyboard(
-            $this->getRandomButton('Button Text 1'),
-            $this->getRandomButton('Button Text 2'),
-            $this->getRandomButton('Button Text 3')
-        ))->getProperty('inline_keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 2', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 3', $keyboard[2][0]->getText());
-
-        $keyboard = (new InlineKeyboard(
-            [$this->getRandomButton('Button Text 4')],
-            [$this->getRandomButton('Button Text 5')],
-            [$this->getRandomButton('Button Text 6')]
-        ))->getProperty('inline_keyboard');
-        self::assertSame('Button Text 4', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 5', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 6', $keyboard[2][0]->getText());
-    }
-
-    public function testInlineKeyboardMultipleButtonsSingleRow(): void
-    {
-        $keyboard = (new InlineKeyboard([
-            $this->getRandomButton('Button Text 1'),
-            $this->getRandomButton('Button Text 2'),
-        ]))->getProperty('inline_keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 2', $keyboard[0][1]->getText());
-    }
-
-    public function testInlineKeyboardMultipleButtonsMultipleRows(): void
-    {
-        $keyboard = (new InlineKeyboard(
-            [
-                $this->getRandomButton('Button Text 1'),
-                $this->getRandomButton('Button Text 2'),
-            ],
-            [
-                $this->getRandomButton('Button Text 3'),
-                $this->getRandomButton('Button Text 4'),
-            ]
-        ))->getProperty('inline_keyboard');
-
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 2', $keyboard[0][1]->getText());
-        self::assertSame('Button Text 3', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 4', $keyboard[1][1]->getText());
-    }
-
-    public function testInlineKeyboardAddRows(): void
-    {
-        $keyboard_obj = new InlineKeyboard([]);
-
-        $keyboard_obj->addRow($this->getRandomButton('Button Text 1'));
-        $keyboard = $keyboard_obj->getProperty('inline_keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-
-        $keyboard_obj->addRow(
-            $this->getRandomButton('Button Text 2'),
-            $this->getRandomButton('Button Text 3')
-        );
-        $keyboard = $keyboard_obj->getProperty('inline_keyboard');
-        self::assertSame('Button Text 2', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 3', $keyboard[1][1]->getText());
-
-        $keyboard_obj->addRow($this->getRandomButton('Button Text 4'));
-        $keyboard = $keyboard_obj->getProperty('inline_keyboard');
-        self::assertSame('Button Text 4', $keyboard[2][0]->getText());
-    }
-}
diff --git a/tests/Unit/Entities/KeyboardButtonTest.php b/tests/Unit/Entities/KeyboardButtonTest.php
deleted file mode 100644
index e7cd907b1..000000000
--- a/tests/Unit/Entities/KeyboardButtonTest.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\KeyboardButton;
-use Longman\TelegramBot\Entities\KeyboardButtonPollType;
-use Longman\TelegramBot\Entities\KeyboardButtonRequestChat;
-use Longman\TelegramBot\Entities\KeyboardButtonRequestUsers;
-use Longman\TelegramBot\Entities\WebAppInfo;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class KeyboardButtonTest extends TestCase
-{
-    public function testKeyboardButtonSuccess(): void
-    {
-        new KeyboardButton(['text' => 'message']);
-        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]);
-        new KeyboardButton(['text' => 'message', 'request_poll' => new KeyboardButtonPollType([])]);
-        new KeyboardButton(['text' => 'message', 'web_app' => new WebAppInfo([])]);
-        self::assertTrue(true);
-    }
-
-    public function testInlineKeyboardButtonCouldBe(): void
-    {
-        self::assertTrue(KeyboardButton::couldBe(['text' => 'message']));
-        self::assertFalse(KeyboardButton::couldBe(['no_text' => 'message']));
-    }
-
-    public function testReturnsSubentitiesOnArray()
-    {
-        $button = new KeyboardButton('message');
-        $button->request_users = [];
-        $this->assertInstanceOf(KeyboardButtonRequestUsers::class, $button->getRequestUsers());
-
-        $button = new KeyboardButton('message');
-        $button->request_chat = [];
-        $this->assertInstanceOf(KeyboardButtonRequestChat::class, $button->getRequestChat());
-
-        $button = new KeyboardButton('message');
-        $button->request_poll = [];
-        $this->assertInstanceOf(KeyboardButtonPollType::class, $button->getRequestPoll());
-
-        $button = new KeyboardButton('message');
-        $button->web_app = [];
-        $this->assertInstanceOf(WebAppInfo::class, $button->getWebApp());
-    }
-}
diff --git a/tests/Unit/Entities/KeyboardTest.php b/tests/Unit/Entities/KeyboardTest.php
deleted file mode 100644
index c5afa90e9..000000000
--- a/tests/Unit/Entities/KeyboardTest.php
+++ /dev/null
@@ -1,208 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Keyboard;
-use Longman\TelegramBot\Entities\KeyboardButton;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class KeyboardTest extends TestCase
-{
-    public function testKeyboardDataMalformedField(): void
-    {
-        $this->expectException(TelegramException::class);
-        $this->expectExceptionMessage('keyboard field is not an array!');
-        new Keyboard(['keyboard' => 'wrong']);
-    }
-
-    public function testKeyboardDataMalformedSubfield(): void
-    {
-        $this->expectException(TelegramException::class);
-        $this->expectExceptionMessage('keyboard subfield is not an array!');
-        new Keyboard(['keyboard' => ['wrong']]);
-    }
-
-    public function testKeyboardSingleButtonSingleRow(): void
-    {
-        $keyboard = (new Keyboard('Button Text 1'))->getProperty('keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-
-        $keyboard = (new Keyboard(['Button Text 2']))->getProperty('keyboard');
-        self::assertSame('Button Text 2', $keyboard[0][0]->getText());
-    }
-
-    public function testKeyboardSingleButtonMultipleRows(): void
-    {
-        $keyboard = (new Keyboard(
-            'Button Text 1',
-            'Button Text 2',
-            'Button Text 3'
-        ))->getProperty('keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 2', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 3', $keyboard[2][0]->getText());
-
-        $keyboard = (new Keyboard(
-            ['Button Text 4'],
-            ['Button Text 5'],
-            ['Button Text 6']
-        ))->getProperty('keyboard');
-        self::assertSame('Button Text 4', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 5', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 6', $keyboard[2][0]->getText());
-    }
-
-    public function testKeyboardMultipleButtonsSingleRow(): void
-    {
-        $keyboard = (new Keyboard(['Button Text 1', 'Button Text 2']))->getProperty('keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 2', $keyboard[0][1]->getText());
-    }
-
-    public function testKeyboardMultipleButtonsMultipleRows(): void
-    {
-        $keyboard = (new Keyboard(
-            ['Button Text 1', 'Button Text 2'],
-            ['Button Text 3', 'Button Text 4']
-        ))->getProperty('keyboard');
-
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 2', $keyboard[0][1]->getText());
-        self::assertSame('Button Text 3', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 4', $keyboard[1][1]->getText());
-    }
-
-    public function testKeyboardWithButtonObjects(): void
-    {
-        $keyboard = (new Keyboard(
-            new KeyboardButton('Button Text 1')
-        ))->getProperty('keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-
-        $keyboard = (new Keyboard(
-            new KeyboardButton('Button Text 2'),
-            new KeyboardButton('Button Text 3')
-        ))->getProperty('keyboard');
-        self::assertSame('Button Text 2', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 3', $keyboard[1][0]->getText());
-
-        $keyboard = (new Keyboard(
-            [new KeyboardButton('Button Text 4')],
-            [new KeyboardButton('Button Text 5'), new KeyboardButton('Button Text 6')]
-        ))->getProperty('keyboard');
-        self::assertSame('Button Text 4', $keyboard[0][0]->getText());
-        self::assertSame('Button Text 5', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 6', $keyboard[1][1]->getText());
-    }
-
-    public function testKeyboardWithDataArray(): void
-    {
-        $resize_keyboard         = (bool) mt_rand(0, 1);
-        $one_time_keyboard       = (bool) mt_rand(0, 1);
-        $input_field_placeholder = 'placeholder';
-        $selective               = (bool) mt_rand(0, 1);
-
-        $keyboard_obj = new Keyboard([
-            'resize_keyboard'         => $resize_keyboard,
-            'one_time_keyboard'       => $one_time_keyboard,
-            'input_field_placeholder' => $input_field_placeholder,
-            'selective'               => $selective,
-            'keyboard'                => [['Button Text 1']],
-        ]);
-
-        $keyboard = $keyboard_obj->getProperty('keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-
-        self::assertSame($resize_keyboard, $keyboard_obj->getResizeKeyboard());
-        self::assertSame($one_time_keyboard, $keyboard_obj->getOneTimeKeyboard());
-        self::assertSame($input_field_placeholder, $keyboard_obj->getInputFieldPlaceholder());
-        self::assertSame($selective, $keyboard_obj->getSelective());
-    }
-
-    public function testPredefinedKeyboards(): void
-    {
-        $keyboard_remove = Keyboard::remove();
-        self::assertTrue($keyboard_remove->getProperty('remove_keyboard'));
-
-        $keyboard_force_reply = Keyboard::forceReply();
-        self::assertTrue($keyboard_force_reply->getProperty('force_reply'));
-    }
-
-    public function testKeyboardMethods(): void
-    {
-        $keyboard_obj = new Keyboard([]);
-
-        self::assertEmpty($keyboard_obj->getOneTimeKeyboard());
-        self::assertEmpty($keyboard_obj->getResizeKeyboard());
-        self::assertEmpty($keyboard_obj->getSelective());
-
-        $keyboard_obj->setOneTimeKeyboard(true);
-        self::assertTrue($keyboard_obj->getOneTimeKeyboard());
-        $keyboard_obj->setOneTimeKeyboard(false);
-        self::assertFalse($keyboard_obj->getOneTimeKeyboard());
-
-        $keyboard_obj->setResizeKeyboard(true);
-        self::assertTrue($keyboard_obj->getResizeKeyboard());
-        $keyboard_obj->setResizeKeyboard(false);
-        self::assertFalse($keyboard_obj->getResizeKeyboard());
-
-        $keyboard_obj->setSelective(true);
-        self::assertTrue($keyboard_obj->getSelective());
-        $keyboard_obj->setSelective(false);
-        self::assertFalse($keyboard_obj->getSelective());
-    }
-
-    public function testKeyboardAddRows(): void
-    {
-        $keyboard_obj = new Keyboard([]);
-
-        $keyboard_obj->addRow('Button Text 1');
-        $keyboard = $keyboard_obj->getProperty('keyboard');
-        self::assertSame('Button Text 1', $keyboard[0][0]->getText());
-
-        $keyboard_obj->addRow('Button Text 2', 'Button Text 3');
-        $keyboard = $keyboard_obj->getProperty('keyboard');
-        self::assertSame('Button Text 2', $keyboard[1][0]->getText());
-        self::assertSame('Button Text 3', $keyboard[1][1]->getText());
-
-        $keyboard_obj->addRow(['text' => 'Button Text 4']);
-        $keyboard = $keyboard_obj->getProperty('keyboard');
-        self::assertSame('Button Text 4', $keyboard[2][0]->getText());
-    }
-
-    public function testSetterMethods(): void
-    {
-        $keyboard = (new Keyboard(
-            [
-                ['text' => 'One'],
-            ]
-        ))->setResizeKeyboard(true);
-
-        $array = json_decode($keyboard->toJson(), true);
-
-        $this->assertIsArray($array);
-
-        $this->assertArrayHasKey('keyboard', $array);
-        $this->assertArrayHasKey('resize_keyboard', $array);
-
-        $this->assertIsArray($array['keyboard']);
-        $this->assertEquals(true, $array['resize_keyboard']);
-    }
-}
diff --git a/tests/Unit/Entities/LocationTest.php b/tests/Unit/Entities/LocationTest.php
deleted file mode 100644
index 20cb94240..000000000
--- a/tests/Unit/Entities/LocationTest.php
+++ /dev/null
@@ -1,57 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Location;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Baev Nikolay <gametester3d@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class LocationTest extends TestCase
-{
-    private $coordinates;
-
-    public function setUp(): void
-    {
-        $this->coordinates = [
-            'longitude' => (float) mt_rand(10, 69),
-            'latitude'  => (float) mt_rand(10, 48),
-        ];
-    }
-
-    public function testBaseStageLocation(): void
-    {
-        $location = new Location($this->coordinates);
-        self::assertInstanceOf(Location::class, $location);
-    }
-
-    public function testGetLongitude(): void
-    {
-        $location = new Location($this->coordinates);
-        $long     = $location->getLongitude();
-        self::assertIsFloat($long);
-        self::assertEquals($this->coordinates['longitude'], $long);
-    }
-
-    public function testGetLatitude(): void
-    {
-        $location = new Location($this->coordinates);
-        $lat      = $location->getLatitude();
-        self::assertIsFloat($lat);
-        self::assertEquals($this->coordinates['latitude'], $lat);
-    }
-}
diff --git a/tests/Unit/Entities/MessageTest.php b/tests/Unit/Entities/MessageTest.php
deleted file mode 100644
index ba73d8146..000000000
--- a/tests/Unit/Entities/MessageTest.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Tests\Unit\TestCase;
-use Longman\TelegramBot\Tests\Unit\TestHelpers;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class MessageTest extends TestCase
-{
-    public function testTextAndCommandRecognise(): void
-    {
-        // /command
-        $message = TestHelpers::getFakeMessageObject(['text' => '/help']);
-        self::assertEquals('/help', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals('/help', $message->getText());
-        self::assertEquals('', $message->getText(true));
-
-        // text
-        $message = TestHelpers::getFakeMessageObject(['text' => 'some text']);
-        self::assertNull($message->getFullCommand());
-        self::assertNull($message->getCommand());
-        self::assertEquals('some text', $message->getText());
-        self::assertEquals('some text', $message->getText(true));
-
-        // /command@bot
-        $message = TestHelpers::getFakeMessageObject(['text' => '/help@testbot']);
-        self::assertEquals('/help@testbot', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals('/help@testbot', $message->getText());
-        self::assertEquals('', $message->getText(true));
-
-        // /command text
-        $message = TestHelpers::getFakeMessageObject(['text' => '/help some text']);
-        self::assertEquals('/help', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals('/help some text', $message->getText());
-        self::assertEquals('some text', $message->getText(true));
-
-        // /command@bot some text
-        $message = TestHelpers::getFakeMessageObject(['text' => '/help@testbot some text']);
-        self::assertEquals('/help@testbot', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals('/help@testbot some text', $message->getText());
-        self::assertEquals('some text', $message->getText(true));
-
-        // /command\n text
-        $message = TestHelpers::getFakeMessageObject(['text' => "/help\n some text"]);
-        self::assertEquals('/help', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals("/help\n some text", $message->getText());
-        self::assertEquals(' some text', $message->getText(true));
-
-        // /command@bot\nsome text
-        $message = TestHelpers::getFakeMessageObject(['text' => "/help@testbot\nsome text"]);
-        self::assertEquals('/help@testbot', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals("/help@testbot\nsome text", $message->getText());
-        self::assertEquals('some text', $message->getText(true));
-
-        // /command@bot \nsome text
-        $message = TestHelpers::getFakeMessageObject(['text' => "/help@testbot \nsome text"]);
-        self::assertEquals('/help@testbot', $message->getFullCommand());
-        self::assertEquals('help', $message->getCommand());
-        self::assertEquals("/help@testbot \nsome text", $message->getText());
-        self::assertEquals("\nsome text", $message->getText(true));
-    }
-
-    public function testGetType(): void
-    {
-        $message = TestHelpers::getFakeMessageObject(['text' => null]);
-        self::assertSame('message', $message->getType());
-
-        $message = TestHelpers::getFakeMessageObject(['text' => '/help']);
-        self::assertSame('command', $message->getType());
-
-        $message = TestHelpers::getFakeMessageObject(['text' => 'some text']);
-        self::assertSame('text', $message->getType());
-    }
-}
diff --git a/tests/Unit/Entities/ReplyToMessageTest.php b/tests/Unit/Entities/ReplyToMessageTest.php
deleted file mode 100644
index 70b6f3f7e..000000000
--- a/tests/Unit/Entities/ReplyToMessageTest.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class ReplyToMessageTest extends TestCase
-{
-    public function testChatType(): void
-    {
-        $json = '{
-            "update_id":137809335,
-            "message":{
-                "message_id":4479,
-                "from":{"id":123,"first_name":"John","username":"MJohn"},
-                "chat":{"id":-123,"title":"MyChat","type":"group"},
-                "date":1449092987,
-                "reply_to_message":{
-                    "message_id":11,
-                    "from":{"id":121,"first_name":"Myname","username":"mybot"},
-                    "chat":{"id":-123,"title":"MyChat","type":"group"},
-                    "date":1449092984,
-                    "text":"type some text"
-                },
-                "text":"some text"
-            }
-        }';
-
-        $update           = new Update(json_decode($json, true), 'mybot');
-        $reply_to_message = $update->getMessage()->getReplyToMessage();
-
-        self::assertNull($reply_to_message->getReplyToMessage());
-    }
-}
diff --git a/tests/Unit/Entities/ServerResponseTest.php b/tests/Unit/Entities/ServerResponseTest.php
deleted file mode 100644
index 6412885d0..000000000
--- a/tests/Unit/Entities/ServerResponseTest.php
+++ /dev/null
@@ -1,386 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- *
- * Written by Marco Boretto
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\File;
-use Longman\TelegramBot\Entities\Message;
-use Longman\TelegramBot\Entities\PhotoSize;
-use Longman\TelegramBot\Entities\ServerResponse;
-use Longman\TelegramBot\Entities\Sticker;
-use Longman\TelegramBot\Entities\StickerSet;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Entities\UserProfilePhotos;
-use Longman\TelegramBot\Request;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-use Longman\TelegramBot\Tests\Unit\TestHelpers;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class ServerResponseTest extends TestCase
-{
-    protected function setUp(): void
-    {
-        // Make sure the current action in the Request class is unset.
-        TestHelpers::setStaticProperty(Request::class, 'current_action', '');
-    }
-
-    public function sendMessageOk(): string
-    {
-        return '{
-            "ok":true,
-            "result":{
-                "message_id":1234,
-                "from":{"id":123456789,"first_name":"botname","username":"namebot"},
-                "chat":{"id":123456789,"first_name":"john","username":"Mjohn"},
-                "date":1441378360,
-                "text":"hello"
-            }
-        }';
-    }
-
-    public function testSendMessageOk(): void
-    {
-        $result        = $this->sendMessageOk();
-        $server        = new ServerResponse(json_decode($result, true), 'testbot');
-        $server_result = $server->getResult();
-
-        self::assertTrue($server->isOk());
-        self::assertNull($server->getErrorCode());
-        self::assertNull($server->getDescription());
-        self::assertInstanceOf(Message::class, $server_result);
-
-        //Message
-        self::assertEquals('1234', $server_result->getMessageId());
-        self::assertEquals('123456789', $server_result->getFrom()->getId());
-        self::assertEquals('botname', $server_result->getFrom()->getFirstName());
-        self::assertEquals('namebot', $server_result->getFrom()->getUsername());
-        self::assertEquals('123456789', $server_result->getChat()->getId());
-        self::assertEquals('john', $server_result->getChat()->getFirstName());
-        self::assertEquals('Mjohn', $server_result->getChat()->getUsername());
-        self::assertEquals('1441378360', $server_result->getDate());
-        self::assertEquals('hello', $server_result->getText());
-
-        //... they are not finished...
-    }
-
-    public function sendMessageFail(): string
-    {
-        return '{
-            "ok":false,
-            "error_code":400,
-            "description":"Error: Bad Request: wrong chat id"
-        }';
-    }
-
-    public function testSendMessageFail(): void
-    {
-        $result = $this->sendMessageFail();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        self::assertFalse($server->isOk());
-        self::assertNull($server->getResult());
-        self::assertEquals('400', $server->getErrorCode());
-        self::assertEquals('Error: Bad Request: wrong chat id', $server->getDescription());
-    }
-
-    public function setWebhookOk(): string
-    {
-        return '{"ok":true,"result":true,"description":"Webhook was set"}';
-    }
-
-    public function testSetWebhookOk(): void
-    {
-        $result = $this->setWebhookOk();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        self::assertTrue($server->isOk());
-        self::assertTrue($server->getResult());
-        self::assertNull($server->getErrorCode());
-        self::assertEquals('Webhook was set', $server->getDescription());
-    }
-
-    public function setWebhookFail(): string
-    {
-        return '{
-            "ok":false,
-            "error_code":400,
-            "description":"Error: Bad request: https:\/\/domain.host.org\/dir\/hook.php"
-        }';
-    }
-
-    public function testSetWebhookFail(): void
-    {
-        $result = $this->setWebhookFail();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        self::assertFalse($server->isOk());
-        self::assertNull($server->getResult());
-        self::assertEquals(400, $server->getErrorCode());
-        self::assertEquals('Error: Bad request: https://domain.host.org/dir/hook.php', $server->getDescription());
-    }
-
-    public function getUpdatesArray(): string
-    {
-        return '{
-            "ok":true,
-            "result":[
-                {
-                    "update_id":123,
-                    "message":{
-                        "message_id":90,
-                        "from":{"id":123456789,"first_name":"John","username":"Mjohn"},
-                        "chat":{"id":123456789,"first_name":"John","username":"Mjohn"},
-                        "date":1441569067,
-                        "text":"\/start"
-                    }
-                },
-                {
-                    "update_id":124,
-                    "message":{
-                        "message_id":91,
-                        "from":{"id":123456788,"first_name":"Patrizia","username":"Patry"},
-                        "chat":{"id":123456788,"first_name":"Patrizia","username":"Patry"},
-                        "date":1441569073,
-                        "text":"Hello!"
-                    }
-                },
-                {
-                    "update_id":125,
-                    "message":{
-                        "message_id":92,
-                        "from":{"id":123456789,"first_name":"John","username":"MJohn"},
-                        "chat":{"id":123456789,"first_name":"John","username":"MJohn"},
-                        "date":1441569094,
-                        "text":"\/echo hello!"
-                    }
-                },
-                {
-                    "update_id":126,
-                    "message":{
-                        "message_id":93,
-                        "from":{"id":123456788,"first_name":"Patrizia","username":"Patry"},
-                        "chat":{"id":123456788,"first_name":"Patrizia","username":"Patry"},
-                        "date":1441569112,
-                        "text":"\/echo the best"
-                    }
-                }
-            ]
-        }';
-    }
-
-    public function testGetUpdatesArray(): void
-    {
-        $result = $this->getUpdatesArray();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        self::assertCount(4, $server->getResult());
-        self::assertInstanceOf(Update::class, $server->getResult()[0]);
-    }
-
-    public function getUpdatesEmpty(): string
-    {
-        return '{"ok":true,"result":[]}';
-    }
-
-    public function testGetUpdatesEmpty(): void
-    {
-        $result = $this->getUpdatesEmpty();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        self::assertEmpty($server->getResult());
-    }
-
-    public function getUserProfilePhotos(): string
-    {
-        TestHelpers::setStaticProperty(Request::class, 'current_action', 'getUserProfilePhotos');
-        return '{
-            "ok":true,
-            "result":{
-                "total_count":3,
-                "photos":[
-                    [
-                        {"file_id":"AgADBG6_vmQaVf3qOGVurBRzHqgg5uEju-8IBAAEC","file_size":7402,"width":160,"height":160},
-                        {"file_id":"AgADBG6_vmQaVf3qOGVurBRzHWMuphij6_MIBAAEC","file_size":15882,"width":320,"height":320},
-                        {"file_id":"AgADBG6_vmQaVf3qOGVurBRzHNWdpQ9jz_cIBAAEC","file_size":46680,"width":640,"height":640}
-                    ],
-                    [
-                        {"file_id":"AgADBAADr6cxG6_vmH-bksDdiYzAABO8UCGz_JLAAgI","file_size":7324,"width":160,"height":160},
-                        {"file_id":"AgADBAADr6cxG6_vmH-bksDdiYzAABAlhB5Q_K0AAgI","file_size":15816,"width":320,"height":320},
-                        {"file_id":"AgADBAADr6cxG6_vmH-bksDdiYzAABIIxOSHyayAAgI","file_size":46620,"width":640,"height":640}
-                    ],
-                    [
-                        {"file_id":"AgABxG6_vmQaL2X0CUTAABMhd1n2RLaRSj6cAAgI","file_size":2710,"width":160,"height":160},
-                        {"file_id":"AgADcxG6_vmQaL2X0EUTAABPXm1og0O7qwjKcAAgI","file_size":11660,"width":320,"height":320},
-                        {"file_id":"AgADxG6_vmQaL2X0CUTAABMOtcfUmoPrcjacAAgI","file_size":37150,"width":640,"height":640}
-                    ]
-                ]
-            }
-        }';
-    }
-
-    public function testGetUserProfilePhotos(): void
-    {
-        $result        = $this->getUserProfilePhotos();
-        $server        = new ServerResponse(json_decode($result, true), 'testbot');
-        $server_result = $server->getResult();
-
-        $photos = $server_result->getPhotos();
-
-        //Photo count
-        self::assertEquals(3, $server_result->getTotalCount());
-        self::assertCount(3, $photos);
-        //Photo size count
-        self::assertCount(3, $photos[0]);
-
-        self::assertInstanceOf(UserProfilePhotos::class, $server_result);
-        self::assertInstanceOf(PhotoSize::class, $photos[0][0]);
-    }
-
-    public function getFile(): string
-    {
-        TestHelpers::setStaticProperty(Request::class, 'current_action', 'getFile');
-        return '{
-            "ok":true,
-            "result":{
-                "file_id":"AgADBxG6_vmQaVf3qRzHYTAABD1hNWdpQ9qz_cIBAAEC",
-                "file_size":46680,
-                "file_path":"photo\/file_1.jpg"
-            }
-        }';
-    }
-
-    public function testGetFile(): void
-    {
-        $result = $this->getFile();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        self::assertInstanceOf(File::class, $server->getResult());
-    }
-
-    public function testSetGeneralTestFakeResponse(): void
-    {
-        //setWebhook ok
-        $fake_response = Request::generateGeneralFakeServerResponse();
-
-        $server = new ServerResponse($fake_response, 'testbot');
-
-        self::assertTrue($server->isOk());
-        self::assertTrue($server->getResult());
-        self::assertNull($server->getErrorCode());
-        self::assertEquals('', $server->getDescription());
-
-        //sendMessage ok
-        $fake_response = Request::generateGeneralFakeServerResponse(['chat_id' => 123456789, 'text' => 'hello']);
-
-        $server = new ServerResponse($fake_response, 'testbot');
-
-        /** @var Message $server_result */
-        $server_result = $server->getResult();
-
-        self::assertTrue($server->isOk());
-        self::assertNull($server->getErrorCode());
-        self::assertNull($server->getDescription());
-        self::assertInstanceOf(Message::class, $server_result);
-
-        //Message
-        self::assertEquals('1234', $server_result->getMessageId());
-        self::assertEquals('1441378360', $server_result->getDate());
-        self::assertEquals('hello', $server_result->getText());
-
-        //Message //User
-        self::assertEquals('123456789', $server_result->getFrom()->getId());
-        self::assertEquals('botname', $server_result->getFrom()->getFirstName());
-        self::assertEquals('namebot', $server_result->getFrom()->getUsername());
-
-        //Message //Chat
-        self::assertEquals('123456789', $server_result->getChat()->getId());
-        self::assertEquals('', $server_result->getChat()->getFirstName());
-        self::assertEquals('', $server_result->getChat()->getUsername());
-
-        //... they are not finished...
-    }
-
-    public function getStickerSet(): string
-    {
-        TestHelpers::setStaticProperty(Request::class, 'current_action', 'getStickerSet');
-        return '{
-            "ok":true,
-            "result":{
-                "name":"stickerset_name",
-                "title":"Some name",
-                "contains_masks":false,
-                "stickers":[
-                    {
-                        "width":512,
-                        "height":324,
-                        "emoji":"\ud83d\ude33",
-                        "set_name":"stickerset_name",
-                        "thumb":{"file_id":"AAQEABOKTFsZAASfA4t3pp1_VlH1AAIC","file_size":3120,"width":128,"height":81},
-                        "file_id":"CAADBAADzAIAAph_7gOATSb9ehxv5QI",
-                        "file_size":14246
-                    },
-                    {
-                        "width":419,
-                        "height":512,
-                        "emoji":"\u2764",
-                        "set_name":"stickerset_name",
-                        "thumb":{"file_id":"AAQEABMj8qoZAASePUHuDSJ2uGIKAAIC","file_size":3500,"width":105,"height":128},
-                        "file_id":"CAADBAADzQIAAph_7gNPFguft4qtjAI",
-                        "file_size":17814
-                    },
-                    {
-                        "width":512,
-                        "height":276,
-                        "emoji":"\ud83d\ude36",
-                        "set_name":"stickerset_name",
-                        "thumb":{"file_id":"AAQEABMiaWcZAATNUEPkYkd0Fh2JBAABAg","file_size":2642,"file_path":"thumbnails\/file_8.jpg","width":128,"height":69},
-                        "file_id":"CAADBAADzwIAAph_7gOClxA3gK5wqAI",
-                        "file_size":12258
-                    },
-                    {
-                        "width":512,
-                        "height":327,
-                        "emoji":"\ud83d\udcbb",
-                        "set_name":"stickerset_name",
-                        "thumb":{"file_id":"AAQEABPC3d8ZAAQUJJnFB1VfII2RAAIC","file_size":3824,"file_path":"thumbnails\/file_10.jpg","width":128,"height":82},
-                        "file_id":"CAADBAAD0QIAAph_7gO-vBJGkTeWqwI",
-                        "file_size":18282
-                    }
-                ]
-            }
-        }';
-    }
-
-    public function testGetStickerSet(): void
-    {
-        $result = $this->getStickerSet();
-        $server = new ServerResponse(json_decode($result, true), 'testbot');
-
-        $server_result = $server->getResult();
-
-        self::assertInstanceOf(StickerSet::class, $server_result);
-        self::assertEquals('stickerset_name', $server_result->getName());
-        self::assertEquals('Some name', $server_result->getTitle());
-        self::assertFalse($server_result->getContainsMasks());
-
-        $stickers = $server_result->getStickers();
-        self::assertCount(4, $stickers);
-        self::assertInstanceOf(Sticker::class, $stickers[0]);
-    }
-}
diff --git a/tests/Unit/Entities/UpdateTest.php b/tests/Unit/Entities/UpdateTest.php
deleted file mode 100644
index f05e59585..000000000
--- a/tests/Unit/Entities/UpdateTest.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class UpdateTest extends TestCase
-{
-    public function testUpdateCast(): void
-    {
-        $json = '{
-            "update_id":137809336,
-            "message":{
-                "message_id":4479,
-                "from":{"id":123,"first_name":"John","username":"MJohn"},
-                "chat":{"id":-123,"title":"MyChat","type":"group"},
-                "date":1449092987,
-                "reply_to_message":{
-                    "message_id":11,
-                    "from":{"id":121,"first_name":"Myname","username":"mybot"},
-                    "chat":{"id":-123,"title":"MyChat","type":"group"},
-                    "date":1449092984,
-                    "text":"type some text"
-                },
-                "text":"some text"
-            }
-        }';
-
-        $struct = json_decode($json, true);
-        $update = new Update($struct, 'mybot');
-
-        $array_string_after = json_decode($update->toJson(), true);
-        self::assertEquals($struct, $array_string_after);
-    }
-}
diff --git a/tests/Unit/Entities/UserTest.php b/tests/Unit/Entities/UserTest.php
deleted file mode 100644
index 41c957ac8..000000000
--- a/tests/Unit/Entities/UserTest.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\User;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class UserTest extends TestCase
-{
-    public function testInstance(): void
-    {
-        $user = new User(['id' => 1]);
-        self::assertInstanceOf(User::class, $user);
-    }
-
-    public function testGetId(): void
-    {
-        $user = new User(['id' => 123]);
-        self::assertEquals(123, $user->getId());
-    }
-
-    public function testTryMention(): void
-    {
-        // Username
-        $user = new User(['id' => 1, 'first_name' => 'John', 'last_name' => 'Taylor', 'username' => 'jtaylor']);
-        self::assertEquals('@jtaylor', $user->tryMention());
-
-        // First name.
-        $user = new User(['id' => 1, 'first_name' => 'John']);
-        self::assertEquals('John', $user->tryMention());
-
-        // First and Last name.
-        $user = new User(['id' => 1, 'first_name' => 'John', 'last_name' => 'Taylor']);
-        self::assertEquals('John Taylor', $user->tryMention());
-    }
-
-    public function testEscapeMarkdown(): void
-    {
-        // Username.
-        $user = new User(['id' => 1, 'first_name' => 'John', 'last_name' => 'Taylor', 'username' => 'j_taylor']);
-        self::assertEquals('@j_taylor', $user->tryMention());
-        self::assertEquals('@j\_taylor', $user->tryMention(true));
-
-        // First name.
-        $user = new User(['id' => 1, 'first_name' => 'John[']);
-        self::assertEquals('John[', $user->tryMention());
-        self::assertEquals('John\[', $user->tryMention(true));
-
-        // First and Last name.
-        $user = new User(['id' => 1, 'first_name' => 'John', 'last_name' => '`Taylor`']);
-        self::assertEquals('John `Taylor`', $user->tryMention());
-        self::assertEquals('John \`Taylor\`', $user->tryMention(true));
-    }
-
-    public function testGetProperties(): void
-    {
-        // Username.
-        $user = new User(['id' => 1, 'username' => 'name_phpunit']);
-        self::assertEquals('name_phpunit', $user->getUsername());
-
-        // First name.
-        $user = new User(['id' => 1, 'first_name' => 'name_phpunit']);
-        self::assertEquals('name_phpunit', $user->getFirstName());
-
-        // Last name.
-        $user = new User(['id' => 1, 'last_name' => 'name_phpunit']);
-        self::assertEquals('name_phpunit', $user->getLastName());
-    }
-}
diff --git a/tests/Unit/Entities/WebhookInfoTest.php b/tests/Unit/Entities/WebhookInfoTest.php
deleted file mode 100644
index 6a931b019..000000000
--- a/tests/Unit/Entities/WebhookInfoTest.php
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit\Entities;
-
-use Longman\TelegramBot\Entities\WebhookInfo;
-use Longman\TelegramBot\Tests\Unit\TestCase;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Baev Nikolay <gametester3d@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class WebhookInfoTest extends TestCase
-{
-    /**
-     * @var array Webhook data
-     */
-    public $data;
-
-    public function setUp(): void
-    {
-        $this->data = [
-            'url'                    => 'http://phpunit',
-            'has_custom_certificate' => (bool) mt_rand(0, 1),
-            'pending_update_count'   => (int) mt_rand(1, 9),
-            'ip_address'             => '1.2.3.4',
-            'last_error_date'        => time(),
-            'last_error_message'     => 'Some_error_message',
-            'max_connections'        => (int) mt_rand(1, 100),
-            'allowed_updates'        => ['message', 'edited_channel_post', 'callback_query'],
-        ];
-    }
-
-    public function testBaseStageWebhookInfo(): void
-    {
-        $webhook = new WebhookInfo($this->data);
-        self::assertInstanceOf(WebhookInfo::class, $webhook);
-    }
-
-    public function testGetUrl(): void
-    {
-        $webhook = new WebhookInfo($this->data);
-        $url     = $webhook->getUrl();
-        self::assertEquals($this->data['url'], $url);
-    }
-
-    public function testGetHasCustomCertificate(): void
-    {
-        $webhook            = new WebhookInfo($this->data);
-        $custom_certificate = $webhook->getHasCustomCertificate();
-        self::assertIsBool($custom_certificate);
-        self::assertEquals($this->data['has_custom_certificate'], $custom_certificate);
-    }
-
-    public function testGetPendingUpdateCount(): void
-    {
-        $webhook      = new WebhookInfo($this->data);
-        $update_count = $webhook->getPendingUpdateCount();
-        self::assertIsInt($update_count);
-        self::assertEquals($this->data['pending_update_count'], $update_count);
-    }
-
-    public function testGetIpAddress(): void
-    {
-        $webhook    = new WebhookInfo($this->data);
-        $ip_address = $webhook->getIpAddress();
-        self::assertIsString($ip_address);
-        self::assertEquals($this->data['ip_address'], $ip_address);
-    }
-
-    public function testGetLastErrorDate(): void
-    {
-        $webhook    = new WebhookInfo($this->data);
-        $error_date = $webhook->getLastErrorDate();
-        self::assertIsInt($error_date);
-        self::assertEquals($this->data['last_error_date'], $error_date);
-    }
-
-    public function testGetLastErrorMessage(): void
-    {
-        $webhook   = new WebhookInfo($this->data);
-        $error_msg = $webhook->getLastErrorMessage();
-        self::assertIsString($error_msg);
-        self::assertEquals($this->data['last_error_message'], $error_msg);
-    }
-
-    public function testGetMaxConnections(): void
-    {
-        $webhook         = new WebhookInfo($this->data);
-        $max_connections = $webhook->getMaxConnections();
-        self::assertIsInt($max_connections);
-        self::assertEquals($this->data['max_connections'], $max_connections);
-    }
-
-    public function testGetAllowedUpdates(): void
-    {
-        $webhook         = new WebhookInfo($this->data);
-        $allowed_updates = $webhook->getAllowedUpdates();
-        self::assertIsArray($allowed_updates);
-        self::assertEquals($this->data['allowed_updates'], $allowed_updates);
-    }
-
-    public function testGetDataWithoutParams(): void
-    {
-        // Make a copy to not risk failed tests if not run in proper order.
-        $data = $this->data;
-
-        unset($data['url']);
-        self::assertNull((new WebhookInfo($data))->getUrl());
-
-        unset($data['has_custom_certificate']);
-        self::assertNull((new WebhookInfo($data))->getHasCustomCertificate());
-
-        unset($data['pending_update_count']);
-        self::assertNull((new WebhookInfo($data))->getPendingUpdateCount());
-
-        unset($data['ip_address']);
-        self::assertNull((new WebhookInfo($data))->getIpAddress());
-
-        unset($data['last_error_date']);
-        self::assertNull((new WebhookInfo($data))->getLastErrorDate());
-
-        unset($data['last_error_message']);
-        self::assertNull((new WebhookInfo($data))->getLastErrorMessage());
-
-        unset($data['max_connections']);
-        self::assertNull((new WebhookInfo($data))->getMaxConnections());
-
-        unset($data['allowed_updates']);
-        self::assertNull((new WebhookInfo($data))->getAllowedUpdates());
-    }
-}
diff --git a/tests/Unit/TelegramLogTest.php b/tests/Unit/TelegramLogTest.php
deleted file mode 100644
index 209cac5f1..000000000
--- a/tests/Unit/TelegramLogTest.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit;
-
-use Longman\TelegramBot\TelegramLog;
-use Monolog\Formatter\LineFormatter;
-use Monolog\Handler\StreamHandler;
-use Monolog\Logger;
-
-/**
- * @package         TelegramTest
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @link            https://github.com/php-telegram-bot/core
- */
-class TelegramLogTest extends TestCase
-{
-    /**
-     * @var array Dummy logfile paths
-     */
-    private static $logfiles = [
-        'debug'  => '/tmp/php-telegram-bot-debug.log',
-        'error'  => '/tmp/php-telegram-bot-error.log',
-        'update' => '/tmp/php-telegram-bot-update.log',
-    ];
-
-    protected function setUp(): void
-    {
-        TelegramLog::initialize(
-            new Logger('bot_log', [
-                (new StreamHandler(self::$logfiles['debug'], Logger::DEBUG))->setFormatter(new LineFormatter(null, null, true)),
-                (new StreamHandler(self::$logfiles['error'], Logger::ERROR))->setFormatter(new LineFormatter(null, null, true)),
-            ]),
-            new Logger('bot_log_updates', [
-                (new StreamHandler(self::$logfiles['update'], Logger::INFO))->setFormatter(new LineFormatter('%message%' . PHP_EOL)),
-            ])
-        );
-    }
-
-    protected function tearDown(): void
-    {
-        // Make sure no logger instance is set after each test.
-        TestHelpers::setStaticProperty(TelegramLog::class, 'logger', null);
-        TestHelpers::setStaticProperty(TelegramLog::class, 'update_logger', null);
-
-        // Make sure no logfiles exist.
-        foreach (self::$logfiles as $file) {
-            file_exists($file) && unlink($file);
-        }
-    }
-
-    public function testNullLogger(): void
-    {
-        TelegramLog::initialize(null, null);
-
-        TelegramLog::debug('my debug log');
-        TelegramLog::error('my error log');
-        TelegramLog::update('my update log');
-
-        foreach (self::$logfiles as $file) {
-            self::assertFileDoesNotExist($file);
-        }
-    }
-
-    public function testDebugStream(): void
-    {
-        $file = self::$logfiles['debug'];
-
-        self::assertFileDoesNotExist($file);
-        TelegramLog::debug('my debug log');
-        TelegramLog::debug('my {place} {holder} debug log', ['place' => 'custom', 'holder' => 'placeholder']);
-
-        self::assertFileExists($file);
-        $debug_log = file_get_contents($file);
-        self::assertStringContainsString('bot_log.DEBUG: my debug log', $debug_log);
-        self::assertStringContainsString('bot_log.DEBUG: my custom placeholder debug log', $debug_log);
-    }
-
-    public function testErrorStream(): void
-    {
-        $file = self::$logfiles['error'];
-
-        self::assertFileDoesNotExist($file);
-        TelegramLog::error('my error log');
-        TelegramLog::error('my {place} {holder} error log', ['place' => 'custom', 'holder' => 'placeholder']);
-
-        self::assertFileExists($file);
-        $error_log = file_get_contents($file);
-        self::assertStringContainsString('bot_log.ERROR: my error log', $error_log);
-        self::assertStringContainsString('bot_log.ERROR: my custom placeholder error log', $error_log);
-    }
-
-    public function testUpdateStream(): void
-    {
-        $file = self::$logfiles['update'];
-
-        self::assertFileDoesNotExist($file);
-        TelegramLog::update('my update log');
-        TelegramLog::update('my {place} {holder} update log', ['place' => 'custom', 'holder' => 'placeholder']);
-
-        self::assertFileExists($file);
-        $update_log = file_get_contents($file);
-        self::assertStringContainsString('my update log', $update_log);
-        self::assertStringContainsString('my custom placeholder update log', $update_log);
-    }
-}
diff --git a/tests/Unit/TelegramTest.php b/tests/Unit/TelegramTest.php
deleted file mode 100644
index 58855d914..000000000
--- a/tests/Unit/TelegramTest.php
+++ /dev/null
@@ -1,261 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit;
-
-use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts;
-use Dummy\AdminCommands\DummyAdminCommand;
-use Dummy\SystemCommands\DummySystemCommand;
-use Dummy\UserCommands\DummyUserCommand;
-use Longman\TelegramBot\Commands\UserCommands\StartCommand;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Exception\TelegramException;
-use Longman\TelegramBot\Telegram;
-use Longman\TelegramBot\TelegramLog;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class TelegramTest extends TestCase
-{
-    use ArraySubsetAsserts;
-
-    /**
-     * @var Telegram
-     */
-    private $telegram;
-
-    /**
-     * @var array A few dummy custom commands paths
-     */
-    private $custom_commands_paths = [
-        '/tmp/php-telegram-bot-custom-commands-1',
-        '/tmp/php-telegram-bot-custom-commands-2',
-        '/tmp/php-telegram-bot-custom-commands-3',
-    ];
-
-    protected function setUp(): void
-    {
-        $this->telegram = new Telegram(self::$dummy_api_key, 'testbot');
-
-        // Create a few dummy custom commands paths.
-        foreach ($this->custom_commands_paths as $custom_path) {
-            mkdir($custom_path);
-        }
-    }
-
-    protected function tearDown(): void
-    {
-        // Clean up the custom commands paths.
-        foreach ($this->custom_commands_paths as $custom_path) {
-            rmdir($custom_path);
-        }
-    }
-
-    public function testNewInstanceWithoutApiKeyParam(): void
-    {
-        $this->expectException(TelegramException::class);
-        $this->expectExceptionMessage('API KEY not defined!');
-        new Telegram('');
-    }
-
-    public function testNewInstanceWithInvalidApiKeyParam(): void
-    {
-        $this->expectException(TelegramException::class);
-        $this->expectExceptionMessage('Invalid API KEY defined!');
-        new Telegram('invalid-api-key-format');
-    }
-
-    public function testGetApiKey(): void
-    {
-        self::assertEquals(self::$dummy_api_key, $this->telegram->getApiKey());
-    }
-
-    public function testGetBotUsername(): void
-    {
-        self::assertEquals('testbot', $this->telegram->getBotUsername());
-    }
-
-    public function testEnableAdmins(): void
-    {
-        $tg = $this->telegram;
-
-        self::assertEmpty($tg->getAdminList());
-
-        // Single
-        $tg->enableAdmin(1);
-        self::assertCount(1, $tg->getAdminList());
-
-        // Multiple
-        $tg->enableAdmins([2, 3]);
-        self::assertCount(3, $tg->getAdminList());
-
-        // Already added
-        $tg->enableAdmin(2);
-        self::assertCount(3, $tg->getAdminList());
-    }
-
-    public function testAddCustomCommandsPaths(): void
-    {
-        $tg = $this->telegram;
-
-        self::assertCount(1, $tg->getCommandsPaths());
-
-        $tg->addCommandsPath($this->custom_commands_paths[0]);
-        self::assertCount(2, $tg->getCommandsPaths());
-        self::assertArraySubset(
-            [$this->custom_commands_paths[0]],
-            $tg->getCommandsPaths()
-        );
-
-        $tg->addCommandsPath('/invalid/path');
-        self::assertCount(2, $tg->getCommandsPaths());
-
-        $tg->addCommandsPaths([
-            $this->custom_commands_paths[1],
-            $this->custom_commands_paths[2],
-        ]);
-        self::assertCount(4, $tg->getCommandsPaths());
-        self::assertArraySubset(
-            array_reverse($this->custom_commands_paths),
-            $tg->getCommandsPaths()
-        );
-
-        $tg->addCommandsPath($this->custom_commands_paths[0]);
-        self::assertCount(4, $tg->getCommandsPaths());
-    }
-
-    public function testAddCustomCommandsClass(): void
-    {
-        $tg = $this->telegram;
-
-        // Require dummy commands to test with
-        require_once __DIR__ . '/Commands/CustomTestCommands/DummySystemCommand.php';
-        require_once __DIR__ . '/Commands/CustomTestCommands/DummyAdminCommand.php';
-        require_once __DIR__ . '/Commands/CustomTestCommands/DummyUserCommand.php';
-
-        // Test for base arrays (System, Admin, User)
-        self::assertCount(3, $tg->getCommandClasses());
-
-        // Test for invalid command classes
-        try {
-            $tg->addCommandClass('');
-        } catch (\InvalidArgumentException $ex) {
-        }
-        self::assertEmpty(array_filter($tg->getCommandClasses()));
-
-        try {
-            $tg->addCommandClass('not\exist\Class');
-        } catch (\InvalidArgumentException $ex) {
-        }
-        self::assertEmpty(array_filter($tg->getCommandClasses()));
-
-        // Add valid command classes
-        $tg->addCommandClass(DummySystemCommand::class);
-        $tg->addCommandClasses([
-            DummyAdminCommand::class,
-            DummyUserCommand::class,
-        ]);
-
-        $command_classes = $tg->getCommandClasses();
-        self::assertSame(['dummy_system' => 'Dummy\SystemCommands\DummySystemCommand'], $command_classes['System']);
-        self::assertSame(['dummy_admin' => 'Dummy\AdminCommands\DummyAdminCommand'], $command_classes['Admin']);
-        self::assertSame(['dummy_user' => 'Dummy\UserCommands\DummyUserCommand'], $command_classes['User']);
-    }
-
-    public function testSettingDownloadUploadPaths(): void
-    {
-        self::assertEmpty($this->telegram->getDownloadPath());
-        self::assertEmpty($this->telegram->getUploadPath());
-
-        $this->telegram->setDownloadPath('/down/below');
-        $this->telegram->setUploadPath('/up/above');
-
-        self::assertSame('/down/below', $this->telegram->getDownloadPath());
-        self::assertSame('/up/above', $this->telegram->getUploadPath());
-    }
-
-    public function testGetCommandsList(): void
-    {
-        $commands = $this->telegram->getCommandsList();
-        self::assertIsArray($commands);
-        self::assertNotCount(0, $commands);
-    }
-
-    public function testGetCommandClass(): void
-    {
-        $className = StartCommand::class;
-        $commands  = $this->telegram->getCommandClasses();
-        self::assertIsArray($commands);
-        self::assertCount(3, $commands);
-
-        $class = $this->telegram->getCommandClassName('user', 'notexist');
-        self::assertNull($class);
-
-        $this->telegram->addCommandClass($className);
-        $class = $this->telegram->getCommandClassName('user', 'start');
-        self::assertNotNull($class);
-
-        self::assertSame($className, $class);
-    }
-
-    public function testUpdateFilter(): void
-    {
-        $rawUpdate = '{
-            "update_id": 513400512,
-            "message": {
-                "message_id": 3,
-                "from": {
-                    "id": 313534466,
-                    "first_name": "first",
-                    "last_name": "last",
-                    "username": "username"
-                },
-                "chat": {
-                    "id": 313534466,
-                    "first_name": "first",
-                    "last_name": "last",
-                    "username": "username",
-                    "type": "private"
-                },
-                "date": 1499402829,
-                "text": "hi"
-            }
-        }';
-
-        $debug_log_file = '/tmp/php-telegram-bot-update-filter-debug.log';
-        TelegramLog::initialize(
-            new \Monolog\Logger('bot_log', [
-                (new \Monolog\Handler\StreamHandler($debug_log_file, \Monolog\Logger::DEBUG))->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true)),
-            ])
-        );
-
-        $update = new Update(json_decode($rawUpdate, true), $this->telegram->getBotUsername());
-        $this->telegram->setUpdateFilter(function (Update $update, Telegram $telegram, &$reason) {
-            if ($update->getMessage()->getChat()->getId() === 313534466) {
-                $reason = 'Invalid user, update denied.';
-                return false;
-            }
-            return true;
-        });
-        $response = $this->telegram->processUpdate($update);
-        self::assertFalse($response->isOk());
-
-        // Check that the reason is written to the debug log.
-        $debug_log = file_get_contents($debug_log_file);
-        self::assertStringContainsString('Invalid user, update denied.', $debug_log);
-        file_exists($debug_log_file) && unlink($debug_log_file);
-    }
-}
diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php
deleted file mode 100644
index fe3ccbecb..000000000
--- a/tests/Unit/TestCase.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit;
-
-use PHPUnit\Framework\TestCase as BaseTestCase;
-
-class TestCase extends BaseTestCase
-{
-    /**
-     * @var string
-     */
-    public static $dummy_api_key = '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11';
-
-    protected function skip64BitTest(): void
-    {
-        if (PHP_INT_SIZE === 4) {
-            self::markTestSkipped(
-                'Skipping test that can run only on a 64-bit build of PHP.'
-            );
-        }
-    }
-}
diff --git a/tests/Unit/TestHelpers.php b/tests/Unit/TestHelpers.php
deleted file mode 100644
index 5daa3ec13..000000000
--- a/tests/Unit/TestHelpers.php
+++ /dev/null
@@ -1,251 +0,0 @@
-<?php
-
-/**
- * This file is part of the TelegramBot package.
- *
- * (c) Avtandil Kikabidze aka LONGMAN <akalongman@gmail.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Longman\TelegramBot\Tests\Unit;
-
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\Chat;
-use Longman\TelegramBot\Entities\Message;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Entities\User;
-use Longman\TelegramBot\Exception\TelegramException;
-
-/**
- * @link            https://github.com/php-telegram-bot/core
- * @author          Avtandil Kikabidze <akalongman@gmail.com>
- * @copyright       Avtandil Kikabidze <akalongman@gmail.com>
- * @license         http://opensource.org/licenses/mit-license.php  The MIT License (MIT)
- * @package         TelegramTest
- */
-class TestHelpers
-{
-    /**
-     * Data template of a user.
-     *
-     * @var array
-     */
-    protected static $user_template = [
-        'id'         => 1,
-        'first_name' => 'first',
-        'last_name'  => 'last',
-        'username'   => 'user',
-    ];
-
-    /**
-     * Data template of a chat.
-     *
-     * @var array
-     */
-    protected static $chat_template = [
-        'id'                             => 1,
-        'first_name'                     => 'first',
-        'last_name'                      => 'last',
-        'username'                       => 'name',
-        'type'                           => 'private',
-        'all_members_are_administrators' => false,
-    ];
-
-    /**
-     * Set the value of a private/protected property of an object
-     *
-     * @param object $object   Object that contains the property
-     * @param string $property Name of the property who's value we want to set
-     * @param mixed  $value    The value to set to the property
-     *
-     * @throws \ReflectionException
-     */
-    public static function setObjectProperty(object $object, string $property, $value): void
-    {
-        $ref_object   = new \ReflectionObject($object);
-        $ref_property = $ref_object->getProperty($property);
-        $ref_property->setAccessible(true);
-        $ref_property->setValue($object, $value);
-    }
-
-    /**
-     * Set the value of a private/protected static property of a class
-     *
-     * @param string $class    Class that contains the static property
-     * @param string $property Name of the property who's value we want to set
-     * @param mixed  $value    The value to set to the property
-     *
-     * @throws \ReflectionException
-     */
-    public static function setStaticProperty(string $class, string $property, $value): void
-    {
-        $ref_property = new \ReflectionProperty($class, $property);
-        $ref_property->setAccessible(true);
-        $ref_property->setValue(null, $value);
-    }
-
-    /**
-     * Return a simple fake Update object
-     *
-     * @param array $data Pass custom data array if needed
-     *
-     * @return Update
-     */
-    public static function getFakeUpdateObject(array $data = []): Update
-    {
-        $data = $data ?: [
-            'update_id' => mt_rand(),
-            'message'   => [
-                'message_id' => mt_rand(),
-                'chat'       => [
-                    'id' => mt_rand(),
-                ],
-                'date'       => time(),
-            ],
-        ];
-        return new Update($data, 'testbot');
-    }
-
-    /**
-     * Return a fake command object for the passed command text
-     *
-     * @param string $command_text
-     *
-     * @return Update
-     */
-    public static function getFakeUpdateCommandObject(string $command_text): Update
-    {
-        $data = [
-            'update_id' => mt_rand(),
-            'message'   => [
-                'message_id' => mt_rand(),
-                'from'       => self::$user_template,
-                'chat'       => self::$chat_template,
-                'date'       => time(),
-                'text'       => $command_text,
-            ],
-        ];
-        return self::getFakeUpdateObject($data);
-    }
-
-    /**
-     * Return a fake user object.
-     *
-     * @param array $data Pass custom data array if needed
-     *
-     * @return User
-     */
-    public static function getFakeUserObject(array $data = []): User
-    {
-        ($data === null) && $data = [];
-
-        return new User($data + self::$user_template);
-    }
-
-    /**
-     * Return a fake chat object.
-     *
-     * @param array $data Pass custom data array if needed
-     *
-     * @return Chat
-     */
-    public static function getFakeChatObject(array $data = []): Chat
-    {
-        return new Chat($data + self::$chat_template);
-    }
-
-    /**
-     * Get fake recorded audio track
-     *
-     * @return array
-     */
-    public static function getFakeRecordedAudio(): array
-    {
-        $mime_type = ['audio/ogg', 'audio/mpeg', 'audio/vnd.wave', 'audio/x-ms-wma', 'audio/basic'];
-        return [
-            'file_id'   => mt_rand(1, 999),
-            'duration'  => mt_rand(1, 99) . ':' . mt_rand(1, 60),
-            'performer' => 'phpunit',
-            'title'     => 'track from phpunit',
-            'mime_type' => $mime_type[array_rand($mime_type, 1)],
-            'file_size' => mt_rand(1, 99999),
-        ];
-    }
-
-    /**
-     * Return a fake message object using the passed ids.
-     *
-     * @param array $message_data Pass custom message data array if needed
-     * @param array $user_data    Pass custom user data array if needed
-     * @param array $chat_data    Pass custom chat data array if needed
-     *
-     * @return Message
-     */
-    public static function getFakeMessageObject(array $message_data = [], array $user_data = [], array $chat_data = []): Message
-    {
-        return new Message($message_data + [
-                'message_id' => mt_rand(),
-                'from'       => $user_data + self::$user_template,
-                'chat'       => $chat_data + self::$chat_template,
-                'date'       => time(),
-                'text'       => 'dummy',
-            ], 'testbot');
-    }
-
-    /**
-     * Start a fake conversation for the passed command and return the randomly generated ids.
-     *
-     * @return array|false
-     */
-    public static function startFakeConversation()
-    {
-        if (!DB::isDbConnected()) {
-            return false;
-        }
-
-        //Just get some random values.
-        $message_id = mt_rand();
-        $user_id    = mt_rand();
-        $chat_id    = mt_rand();
-
-        try {
-            //Make sure we have a valid user and chat available.
-            $message = self::getFakeMessageObject(['message_id' => $message_id], ['id' => $user_id], ['id' => $chat_id]);
-            DB::insertMessageRequest($message);
-            DB::insertUser($message->getFrom(), null, $message->getChat());
-
-            return compact('message_id', 'user_id', 'chat_id');
-        } catch (TelegramException $e) {
-            return false;
-        }
-    }
-
-    /**
-     * Empty all tables for the passed database
-     *
-     * @param array $credentials
-     */
-    public static function emptyDb(array $credentials): void
-    {
-        $dsn = 'mysql:host=' . $credentials['host'] . ';dbname=' . $credentials['database'];
-        if (!empty($credentials['port'])) {
-            $dsn .= ';port=' . $credentials['port'];
-        }
-
-        $options = [\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'];
-
-        $pdo = new \PDO($dsn, $credentials['user'], $credentials['password'], $options);
-        $pdo->prepare('
-            DELETE FROM `conversation`;
-            DELETE FROM `telegram_update`;
-            DELETE FROM `chosen_inline_result`;
-            DELETE FROM `inline_query`;
-            DELETE FROM `message`;
-            DELETE FROM `user_chat`;
-            DELETE FROM `chat`;
-            DELETE FROM `user`;
-        ')->execute();
-    }
-}
diff --git a/utils/db-schema-update/0.44.1-0.45.0.sql b/utils/db-schema-update/0.44.1-0.45.0.sql
deleted file mode 100644
index 98630c2b1..000000000
--- a/utils/db-schema-update/0.44.1-0.45.0.sql
+++ /dev/null
@@ -1,4 +0,0 @@
-ALTER TABLE `user` ADD COLUMN `language_code` CHAR(10) DEFAULT NULL COMMENT 'User''s system language' AFTER `username`;
-ALTER TABLE `message` ADD COLUMN `video_note` TEXT COMMENT 'VoiceNote Object. Message is a Video Note, information about the Video Note' AFTER `voice`;
-ALTER TABLE `message` ADD COLUMN `new_chat_members` TEXT COMMENT 'List of unique user identifiers, new member(s) were added to the group, information about them (one of these members may be the bot itself)' AFTER `new_chat_member`;
-UPDATE `message` SET `new_chat_members` = `new_chat_member`;
diff --git a/utils/db-schema-update/0.47.1-0.48.0.sql b/utils/db-schema-update/0.47.1-0.48.0.sql
deleted file mode 100644
index 85131217c..000000000
--- a/utils/db-schema-update/0.47.1-0.48.0.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE `user` ADD COLUMN `is_bot` tinyint(1) DEFAULT 0 COMMENT 'True if this user is a bot' AFTER `id`;
diff --git a/utils/db-schema-update/0.50.0-0.51.0.sql b/utils/db-schema-update/0.50.0-0.51.0.sql
deleted file mode 100644
index 86cae0a0e..000000000
--- a/utils/db-schema-update/0.50.0-0.51.0.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `media_group_id` TEXT COMMENT 'The unique identifier of a media message group this message belongs to' AFTER `reply_to_message`;
diff --git a/utils/db-schema-update/0.52.0-0.53.0.sql b/utils/db-schema-update/0.52.0-0.53.0.sql
deleted file mode 100644
index 4b95fafac..000000000
--- a/utils/db-schema-update/0.52.0-0.53.0.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `connected_website` TEXT NULL COMMENT 'The domain name of the website on which the user has logged in.' AFTER `pinned_message`;
diff --git a/utils/db-schema-update/0.53.0-0.54.0.sql b/utils/db-schema-update/0.53.0-0.54.0.sql
deleted file mode 100644
index 413978618..000000000
--- a/utils/db-schema-update/0.53.0-0.54.0.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `game` TEXT NULL COMMENT 'Message is a game, information about the game.' AFTER `document`;
diff --git a/utils/db-schema-update/0.54.1-0.55.0.sql b/utils/db-schema-update/0.54.1-0.55.0.sql
deleted file mode 100644
index 531465bee..000000000
--- a/utils/db-schema-update/0.54.1-0.55.0.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `animation` TEXT NULL COMMENT 'Message is an animation, information about the animation' AFTER `document`;
-ALTER TABLE `message` ADD COLUMN `passport_data` TEXT NULL COMMENT 'Telegram Passport data' AFTER `connected_website`;
diff --git a/utils/db-schema-update/0.56.0-0.57.0.sql b/utils/db-schema-update/0.56.0-0.57.0.sql
deleted file mode 100644
index 120e33721..000000000
--- a/utils/db-schema-update/0.56.0-0.57.0.sql
+++ /dev/null
@@ -1,69 +0,0 @@
-ALTER TABLE `chat` ADD COLUMN `first_name` CHAR(255) DEFAULT NULL COMMENT 'First name of the other party in a private chat' AFTER `username`;
-ALTER TABLE `chat` ADD COLUMN `last_name` CHAR(255) DEFAULT NULL COMMENT 'Last name of the other party in a private chat' AFTER `first_name`;
-ALTER TABLE `message` ADD COLUMN `forward_signature` TEXT NULL DEFAULT NULL COMMENT 'For messages forwarded from channels, signature of the post author if present' AFTER `forward_from_message_id`;
-ALTER TABLE `message` ADD COLUMN `forward_sender_name` TEXT NULL DEFAULT NULL COMMENT 'Sender''s name for messages forwarded from users who disallow adding a link to their account in forwarded messages' AFTER `forward_signature`;
-ALTER TABLE `message` ADD COLUMN `edit_date` bigint UNSIGNED DEFAULT NULL COMMENT 'Date the message was last edited in Unix time' AFTER `reply_to_message`;
-ALTER TABLE `message` ADD COLUMN `author_signature` TEXT COMMENT 'Signature of the post author for messages in channels' AFTER `media_group_id`;
-ALTER TABLE `message` ADD COLUMN `caption_entities` TEXT COMMENT 'For messages with a caption, special entities like usernames, URLs, bot commands, etc. that appear in the caption';
-ALTER TABLE `message` ADD COLUMN `poll` TEXT COMMENT 'Poll object. Message is a native poll, information about the poll' AFTER `venue`;
-ALTER TABLE `message` ADD COLUMN `invoice` TEXT NULL COMMENT 'Message is an invoice for a payment, information about the invoice' AFTER `pinned_message`;
-ALTER TABLE `message` ADD COLUMN `successful_payment` TEXT NULL COMMENT 'Message is a service message about a successful payment, information about the payment' AFTER `invoice`;
-ALTER TABLE `callback_query` ADD COLUMN `chat_instance` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Global identifier, uniquely corresponding to the chat to which the message with the callback button was sent' AFTER `inline_message_id`;
-ALTER TABLE `callback_query` ADD COLUMN `game_short_name` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Short name of a Game to be returned, serves as the unique identifier for the game' AFTER `data`;
-
-CREATE TABLE IF NOT EXISTS `shipping_query` (
-  `id` bigint UNSIGNED COMMENT 'Unique query identifier',
-  `user_id` bigint COMMENT 'User who sent the query',
-  `invoice_payload` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Bot specified invoice payload',
-  `shipping_address` CHAR(255) NOT NULL DEFAULT '' COMMENT 'User specified shipping address',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `pre_checkout_query` (
-  `id` bigint UNSIGNED COMMENT 'Unique query identifier',
-  `user_id` bigint COMMENT 'User who sent the query',
-  `currency` CHAR(3) COMMENT 'Three-letter ISO 4217 currency code',
-  `total_amount` bigint COMMENT 'Total price in the smallest units of the currency',
-  `invoice_payload` CHAR(255) NOT NULL DEFAULT '' COMMENT 'Bot specified invoice payload',
-  `shipping_option_id` CHAR(255) NULL COMMENT 'Identifier of the shipping option chosen by the user',
-  `order_info` TEXT NULL COMMENT 'Order info provided by the user',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`),
-  KEY `user_id` (`user_id`),
-
-  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-CREATE TABLE IF NOT EXISTS `poll` (
-  `id` bigint UNSIGNED COMMENT 'Unique poll identifier',
-  `question` char(255) NOT NULL COMMENT 'Poll question',
-  `options` text NOT NULL COMMENT 'List of poll options',
-  `is_closed` tinyint(1) DEFAULT 0 COMMENT 'True, if the poll is closed',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-ALTER TABLE `telegram_update` ADD COLUMN `channel_post_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming channel post of any kind - text, photo, sticker, etc.';
-ALTER TABLE `telegram_update` ADD COLUMN `edited_channel_post_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New version of a channel post that is known to the bot and was edited';
-ALTER TABLE `telegram_update` ADD COLUMN `shipping_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming shipping query. Only for invoices with flexible price';
-ALTER TABLE `telegram_update` ADD COLUMN `pre_checkout_query_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New incoming pre-checkout query. Contains full information about checkout';
-ALTER TABLE `telegram_update` ADD COLUMN `poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'New poll state. Bots receive only updates about polls, which are sent or stopped by the bot';
-
-ALTER TABLE `telegram_update` ADD KEY `channel_post_id` (`channel_post_id`);
-ALTER TABLE `telegram_update` ADD KEY `edited_channel_post_id` (`edited_channel_post_id`);
-ALTER TABLE `telegram_update` ADD KEY `shipping_query_id` (`shipping_query_id`);
-ALTER TABLE `telegram_update` ADD KEY `pre_checkout_query_id` (`pre_checkout_query_id`);
-ALTER TABLE `telegram_update` ADD KEY `poll_id` (`poll_id`);
-
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`chat_id`, `channel_post_id`) REFERENCES `message` (`chat_id`, `id`);
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`edited_channel_post_id`) REFERENCES `edited_message` (`id`);
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`shipping_query_id`) REFERENCES `shipping_query` (`id`);
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`pre_checkout_query_id`) REFERENCES `pre_checkout_query` (`id`);
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`poll_id`) REFERENCES `poll` (`id`);
diff --git a/utils/db-schema-update/0.57.0-0.58.0.sql b/utils/db-schema-update/0.57.0-0.58.0.sql
deleted file mode 100644
index 9d48cd8e7..000000000
--- a/utils/db-schema-update/0.57.0-0.58.0.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `reply_markup` TEXT NULL COMMENT 'Inline keyboard attached to the message' AFTER `passport_data`;
diff --git a/utils/db-schema-update/0.60.0-0.61.0.sql b/utils/db-schema-update/0.60.0-0.61.0.sql
deleted file mode 100644
index 54b0c449c..000000000
--- a/utils/db-schema-update/0.60.0-0.61.0.sql
+++ /dev/null
@@ -1,10 +0,0 @@
-SET FOREIGN_KEY_CHECKS=0;
-
-ALTER TABLE `telegram_update`
-DROP KEY `message_id`;
-
-ALTER TABLE `telegram_update`
-ADD KEY `message_id` (`message_id`),
-ADD KEY `chat_message_id` (`chat_id`, `message_id`);
-
-SET FOREIGN_KEY_CHECKS=1;
diff --git a/utils/db-schema-update/0.61.1-0.62.0.sql b/utils/db-schema-update/0.61.1-0.62.0.sql
deleted file mode 100644
index a38f361e0..000000000
--- a/utils/db-schema-update/0.61.1-0.62.0.sql
+++ /dev/null
@@ -1,20 +0,0 @@
-ALTER TABLE `poll` ADD COLUMN `total_voter_count` int UNSIGNED COMMENT 'Total number of users that voted in the poll' AFTER `options`;
-ALTER TABLE `poll` ADD COLUMN `is_anonymous` tinyint(1) DEFAULT 1 COMMENT 'True, if the poll is anonymous' AFTER `is_closed`;
-ALTER TABLE `poll` ADD COLUMN `type` char(255) COMMENT 'Poll type, currently can be “regular” or “quiz”' AFTER `is_anonymous`;
-ALTER TABLE `poll` ADD COLUMN `allows_multiple_answers` tinyint(1) DEFAULT 0 COMMENT 'True, if the poll allows multiple answers' AFTER `type`;
-ALTER TABLE `poll` ADD COLUMN `correct_option_id` int UNSIGNED COMMENT '0-based identifier of the correct answer option. Available only for polls in the quiz mode, which are closed, or was sent (not forwarded) by the bot or to the private chat with the bot.' AFTER `allows_multiple_answers`;
-ALTER TABLE `message` ADD COLUMN `dice` TEXT COMMENT 'Message is a dice with random value from 1 to 6' AFTER `poll`;
-
-CREATE TABLE IF NOT EXISTS `poll_answer` (
-  `poll_id` bigint UNSIGNED COMMENT 'Unique poll identifier',
-  `user_id` bigint NOT NULL COMMENT 'The user, who changed the answer to the poll',
-  `option_ids` text NOT NULL COMMENT '0-based identifiers of answer options, chosen by the user. May be empty if the user retracted their vote.',
-  `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-  PRIMARY KEY (`poll_id`),
-  FOREIGN KEY (`poll_id`) REFERENCES `poll` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-ALTER TABLE `telegram_update` ADD COLUMN `poll_answer_poll_id` bigint UNSIGNED DEFAULT NULL COMMENT 'A user changed their answer in a non-anonymous poll. Bots receive new votes only in polls that were sent by the bot itself.' AFTER `poll_id`;
-ALTER TABLE `telegram_update` ADD KEY `poll_answer_poll_id` (`poll_answer_poll_id`);
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`poll_answer_poll_id`) REFERENCES `poll_answer` (`poll_id`);
diff --git a/utils/db-schema-update/0.62.0-0.63.0.sql b/utils/db-schema-update/0.62.0-0.63.0.sql
deleted file mode 100644
index 6e1ce95b3..000000000
--- a/utils/db-schema-update/0.62.0-0.63.0.sql
+++ /dev/null
@@ -1,15 +0,0 @@
-ALTER TABLE `poll` ADD COLUMN `explanation` varchar(255) DEFAULT NULL COMMENT 'Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters' AFTER `correct_option_id`;
-ALTER TABLE `poll` ADD COLUMN `explanation_entities` text DEFAULT NULL COMMENT 'Special entities like usernames, URLs, bot commands, etc. that appear in the explanation' AFTER `explanation`;
-ALTER TABLE `poll` ADD COLUMN `open_period` int UNSIGNED DEFAULT NULL COMMENT 'Amount of time in seconds the poll will be active after creation' AFTER `explanation_entities`;
-ALTER TABLE `poll` ADD COLUMN `close_date` timestamp NULL DEFAULT NULL COMMENT 'Point in time (Unix timestamp) when the poll will be automatically closed' AFTER `open_period`;
-
-ALTER TABLE `poll_answer` DROP PRIMARY KEY, ADD PRIMARY KEY (`poll_id`, `user_id`);
-
-ALTER TABLE `message`
-    DROP FOREIGN KEY IF EXISTS `message_ibfk_6`,
-    DROP INDEX IF EXISTS `message_ibfk_6`;
-
-ALTER TABLE `message`
-    ADD COLUMN `via_bot` bigint NULL DEFAULT NULL COMMENT 'Optional. Bot through which the message was sent' AFTER `reply_to_message`,
-    ADD KEY `via_bot` (`via_bot`),
-    ADD FOREIGN KEY (`via_bot`) REFERENCES `user` (`id`);
diff --git a/utils/db-schema-update/0.64.0-0.70.0.sql b/utils/db-schema-update/0.64.0-0.70.0.sql
deleted file mode 100644
index daa708720..000000000
--- a/utils/db-schema-update/0.64.0-0.70.0.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `sender_chat_id` bigint COMMENT 'Sender of the message, sent on behalf of a chat' AFTER `chat_id`;
-ALTER TABLE `message` ADD COLUMN `proximity_alert_triggered` TEXT NULL COMMENT 'Service message. A user in the chat triggered another user''s proximity alert while sharing Live Location.' AFTER `passport_data`;
-ALTER TABLE `poll` MODIFY `question` text NOT NULL COMMENT 'Poll question';
diff --git a/utils/db-schema-update/0.71.0-0.72.0.sql b/utils/db-schema-update/0.71.0-0.72.0.sql
deleted file mode 100644
index 0720ae591..000000000
--- a/utils/db-schema-update/0.71.0-0.72.0.sql
+++ /dev/null
@@ -1,27 +0,0 @@
-ALTER TABLE `message` MODIFY `edit_date` timestamp NULL DEFAULT NULL COMMENT 'Date the message was last edited in Unix time';
-
-CREATE TABLE IF NOT EXISTS `chat_member_updated` (
-    `id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-    `chat_id` BIGINT NOT NULL COMMENT 'Chat the user belongs to',
-    `user_id` BIGINT NOT NULL COMMENT 'Performer of the action, which resulted in the change',
-    `date` TIMESTAMP NOT NULL COMMENT 'Date the change was done in Unix time',
-    `old_chat_member` TEXT NOT NULL COMMENT 'Previous information about the chat member',
-    `new_chat_member` TEXT NOT NULL COMMENT 'New information about the chat member',
-    `invite_link` TEXT NULL COMMENT 'Chat invite link, which was used by the user to join the chat; for joining by invite link events only',
-    `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-    PRIMARY KEY (`id`),
-
-    FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
-    FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-ALTER TABLE `telegram_update` ADD COLUMN `my_chat_member_updated_id` BIGINT UNSIGNED NULL COMMENT '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.';
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`my_chat_member_updated_id`) REFERENCES `chat_member_updated` (`id`);
-ALTER TABLE `telegram_update` ADD COLUMN `chat_member_updated_id` BIGINT UNSIGNED NULL COMMENT '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.';
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`chat_member_updated_id`) REFERENCES `chat_member_updated` (`id`);
-
-ALTER TABLE `message` ADD COLUMN `message_auto_delete_timer_changed` TEXT COMMENT 'MessageAutoDeleteTimerChanged object. Message is a service message: auto-delete timer settings changed in the chat' AFTER `channel_chat_created`;
-ALTER TABLE `message` ADD COLUMN `voice_chat_started` TEXT COMMENT 'VoiceChatStarted object. Message is a service message: voice chat started' AFTER `proximity_alert_triggered`;
-ALTER TABLE `message` ADD COLUMN `voice_chat_ended` TEXT COMMENT 'VoiceChatEnded object. Message is a service message: voice chat ended' AFTER `voice_chat_started`;
-ALTER TABLE `message` ADD COLUMN `voice_chat_participants_invited` TEXT COMMENT 'VoiceChatParticipantsInvited object. Message is a service message: new participants invited to a voice chat' AFTER `voice_chat_ended`;
diff --git a/utils/db-schema-update/0.72.0-0.73.0.sql b/utils/db-schema-update/0.72.0-0.73.0.sql
deleted file mode 100644
index 2664a990c..000000000
--- a/utils/db-schema-update/0.72.0-0.73.0.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `voice_chat_scheduled` TEXT COMMENT 'VoiceChatScheduled object. Message is a service message: voice chat scheduled' AFTER `proximity_alert_triggered`;
-ALTER TABLE `inline_query` ADD COLUMN `chat_type` CHAR(255) NULL DEFAULT NULL COMMENT 'Optional. Type of the chat, from which the inline query was sent.' AFTER `offset`;
diff --git a/utils/db-schema-update/0.74.0-0.75.0.sql b/utils/db-schema-update/0.74.0-0.75.0.sql
deleted file mode 100644
index 6bbf51422..000000000
--- a/utils/db-schema-update/0.74.0-0.75.0.sql
+++ /dev/null
@@ -1,20 +0,0 @@
-CREATE TABLE IF NOT EXISTS `chat_join_request` (
-    `id` BIGINT UNSIGNED AUTO_INCREMENT COMMENT 'Unique identifier for this entry',
-    `chat_id` BIGINT NOT NULL COMMENT 'Chat to which the request was sent',
-    `user_id` BIGINT NOT NULL COMMENT 'User that sent the join request',
-    `date` TIMESTAMP NOT NULL COMMENT 'Date the request was sent in Unix time',
-    `bio` TEXT NULL COMMENT 'Optional. Bio of the user',
-    `invite_link` TEXT NULL COMMENT 'Optional. Chat invite link that was used by the user to send the join request',
-    `created_at` timestamp NULL DEFAULT NULL COMMENT 'Entry date creation',
-
-    PRIMARY KEY (`id`),
-
-    FOREIGN KEY (`chat_id`) REFERENCES `chat` (`id`),
-    FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
-
-ALTER TABLE `telegram_update` ADD COLUMN `chat_join_request_id` BIGINT UNSIGNED NULL COMMENT 'A request to join the chat has been sent';
-ALTER TABLE `telegram_update` ADD FOREIGN KEY (`chat_join_request_id`) REFERENCES `chat_join_request` (`id`);
-
-ALTER TABLE `message` ADD COLUMN `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' AFTER `forward_date`;
-ALTER TABLE `message` ADD COLUMN `has_protected_content` tinyint(1) DEFAULT 0 COMMENT 'True, if the message can''t be forwarded' AFTER `edit_date`;
diff --git a/utils/db-schema-update/0.76.1-0.77.0.sql b/utils/db-schema-update/0.76.1-0.77.0.sql
deleted file mode 100644
index 56c518489..000000000
--- a/utils/db-schema-update/0.76.1-0.77.0.sql
+++ /dev/null
@@ -1,7 +0,0 @@
-ALTER TABLE `message` ADD COLUMN `web_app_data` TEXT NULL DEFAULT NULL COMMENT 'Service message: data sent by a Web App' AFTER `voice_chat_participants_invited`;
-
-ALTER TABLE `message`
-    CHANGE `voice_chat_scheduled` `video_chat_scheduled` TEXT COMMENT 'Service message: video chat scheduled',
-    CHANGE `voice_chat_started` `video_chat_started` TEXT COMMENT 'Service message: video chat started',
-    CHANGE `voice_chat_ended` `video_chat_ended` TEXT COMMENT 'Service message: video chat ended',
-    CHANGE `voice_chat_participants_invited` `video_chat_participants_invited` TEXT COMMENT 'Service message: new participants invited to a video chat';
diff --git a/utils/db-schema-update/0.77.1-0.78.0.sql b/utils/db-schema-update/0.77.1-0.78.0.sql
deleted file mode 100644
index 370ee6f73..000000000
--- a/utils/db-schema-update/0.77.1-0.78.0.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-ALTER TABLE `user` ADD COLUMN `is_premium` tinyint(1) DEFAULT 0 COMMENT 'True, if this user is a Telegram Premium user' AFTER `language_code`;
-ALTER TABLE `user` ADD COLUMN `added_to_attachment_menu` tinyint(1) DEFAULT 0 COMMENT 'True, if this user added the bot to the attachment menu' AFTER `is_premium`;
diff --git a/utils/db-schema-update/0.79.0-0.80.0.sql b/utils/db-schema-update/0.79.0-0.80.0.sql
deleted file mode 100644
index bba84eb43..000000000
--- a/utils/db-schema-update/0.79.0-0.80.0.sql
+++ /dev/null
@@ -1,9 +0,0 @@
-ALTER TABLE `message`
-    ADD COLUMN `is_topic_message` TINYINT(1) DEFAULT 0 COMMENT 'True, if the message is sent to a forum topic' AFTER `forward_date`,
-    ADD COLUMN `message_thread_id` BIGINT(20) NULL DEFAULT NULL COMMENT 'Unique identifier of a message thread to which the message belongs; for supergroups only' AFTER `id`,
-    ADD COLUMN `forum_topic_created` TEXT NULL DEFAULT NULL COMMENT 'Service message: forum topic created' AFTER `proximity_alert_triggered`,
-    ADD COLUMN `forum_topic_closed` TEXT NULL DEFAULT NULL COMMENT 'Service message: forum topic closed' AFTER `forum_topic_created`,
-    ADD COLUMN `forum_topic_reopened` TEXT NULL DEFAULT NULL COMMENT 'Service message: forum topic reopened' AFTER `forum_topic_closed`;
-
-ALTER TABLE `chat`
-    ADD COLUMN `is_forum` TINYINT(1) DEFAULT 0 COMMENT 'True, if the supergroup chat is a forum (has topics enabled)' AFTER `last_name`;
diff --git a/utils/db-schema-update/0.80.0-0.81.0.sql b/utils/db-schema-update/0.80.0-0.81.0.sql
deleted file mode 100644
index 4411b1201..000000000
--- a/utils/db-schema-update/0.80.0-0.81.0.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-ALTER TABLE `message`
-    ADD COLUMN `has_media_spoiler`            TINYINT(1) DEFAULT 0 COMMENT 'True, if the message media is covered by a spoiler animation' AFTER `caption`,
-    ADD COLUMN `write_access_allowed`         TEXT       DEFAULT NULL COMMENT 'Service message: the user allowed the bot added to the attachment menu to write messages' AFTER `connected_website`,
-    ADD COLUMN `forum_topic_edited`           TEXT       DEFAULT NULL COMMENT 'Service message: forum topic edited' AFTER `forum_topic_created`,
-    ADD COLUMN `general_forum_topic_hidden`   TEXT       DEFAULT NULL COMMENT 'Service message: the General forum topic hidden' AFTER `forum_topic_reopened`,
-    ADD COLUMN `general_forum_topic_unhidden` TEXT       DEFAULT NULL COMMENT 'Service message: the General forum topic unhidden' AFTER `general_forum_topic_hidden`,
-    ADD COLUMN `user_shared`                  TEXT       DEFAULT NULL COMMENT 'Optional. Service message: a user was shared with the bot' AFTER `successful_payment`,
-    ADD COLUMN `chat_shared`                  TEXT       DEFAULT NULL COMMENT 'Optional. Service message: a chat was shared with the bot' AFTER `user_shared`;
diff --git a/utils/db-schema-update/0.81.0-0.82.0.sql b/utils/db-schema-update/0.81.0-0.82.0.sql
deleted file mode 100644
index bae194cb9..000000000
--- a/utils/db-schema-update/0.81.0-0.82.0.sql
+++ /dev/null
@@ -1,2 +0,0 @@
-ALTER TABLE `message`
-    ADD COLUMN `story` TEXT DEFAULT NULL COMMENT 'Story object. Message is a forwarded story' AFTER `sticker`;
diff --git a/utils/db-schema-update/0.82.0-unreleased.sql b/utils/db-schema-update/0.82.0-unreleased.sql
deleted file mode 100644
index 3fb04f377..000000000
--- a/utils/db-schema-update/0.82.0-unreleased.sql
+++ /dev/null
@@ -1,67 +0,0 @@
-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`;
diff --git a/utils/importFromLog.php b/utils/importFromLog.php
deleted file mode 100644
index 634df0260..000000000
--- a/utils/importFromLog.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-
-/**
- * Import all updates from a raw updates log file into the database.
- * Works for both webhook and getUpdates.
- *
- * Modify $updates_log_file_path and $mysql_credentials below!
- *
- * Requires PHP7+
- *
- * @todo Move to dedicated CLI tool.
- */
-
-use Longman\TelegramBot\DB;
-use Longman\TelegramBot\Entities\Update;
-use Longman\TelegramBot\Telegram;
-
-require __DIR__ . '/../vendor/autoload.php';
-
-// This is the file that contains the raw updates.
-$updates_log_file_path = __DIR__ . '/updates.log';
-
-// Credentials of the database to import updates to.
-$mysql_credentials = [
-    'host'     => 'localhost',
-    'port'     => 3306, // optional
-    'user'     => 'dbuser',
-    'password' => 'dbpass',
-    'database' => 'dbname',
-];
-
-try {
-    // Create dummy Telegram API object and connect to MySQL database.
-    (new Telegram('1:A'))->enableMySql($mysql_credentials);
-
-    // Load the updates log file to iterate over.
-    $updates_log_file = new SplFileObject($updates_log_file_path);
-    $updates_log_file->setFlags(SplFileObject::DROP_NEW_LINE | SplFileObject::READ_AHEAD | SplFileObject::SKIP_EMPTY);
-
-    foreach ($updates_log_file as $update_json) {
-        if ($update_arr = json_decode($update_json, true)) {
-            echo $update_json . PHP_EOL;
-
-            // Get all updates on this line.
-            $updates_data = array_filter($update_arr['result'] ?? [$update_arr]);
-            foreach ($updates_data as $update_data) {
-                $update = new Update($update_data);
-                printf(
-                    'Update ID %d %s' . PHP_EOL,
-                    $update->getUpdateId(),
-                    DB::insertRequest($update) ? '(success)' : '(failed) ' . implode(' ', DB::getPdo()->errorInfo())
-                );
-            }
-        }
-    }
-} catch (Throwable $e) {
-    // Output any errors.
-    echo $e;
-}
