Skip to content

Update for Telegram Bot API 10.3 - #10

Merged
luzrain merged 3 commits into
luzrain:masterfrom
Tairesh:api-10.3
Sep 24, 2026
Merged

luzrain merged 3 commits into
luzrain:masterfrom
Tairesh:api-10.3

Conversation

@Tairesh

@Tairesh Tairesh commented Sep 18, 2026

Copy link
Copy Markdown

Bot API 10.3

Brings the library from Bot API 10.1 to 10.3, covering both skipped releases: 10.2 (July 14, 2026) and 10.3 (August 24, 2026).

48 new classes under src/, 36 modified source files, 8 new test files with 5 test doubles and 4 fixtures. The README badge moves to 10.3.

Rich messages

Bot API 10.2 introduced the outgoing half of the rich block system, and 10.3 extended both halves.

New namespace Luzrain\TelegramBotApi\Type\InputRichBlock holds 26 classes: the InputRichBlock base, 24 concrete blocks and InputRichBlockListItem. It mirrors the existing incoming Type\RichBlock family. I kept it separate because merging would leave a single directory of 54 files.

InputRichMessage gains blocks and media. Two new types support them: InputRichMessageMedia addresses media from markdown or HTML through tg://photo?id= links, and InputMediaVoiceNote carries a voice message.

10.3 adds RichMessageButton, which both RichTextButton and RichBlockButtons reference, plus RichBlockExpandableBlockQuotation, RichBlockDocument and their input counterparts. Tables gain is_compact.

Telegram reuses RichBlockCaption, RichBlockTableCell and Location as input types in 10.2. All three had protected constructors, so a caller could not build InputRichBlockPhoto, InputRichBlockTable or InputRichBlockMap. I widened the three constructors to public.

Ephemeral messages

10.2 introduced ephemeral messages and 10.3 replaced their parameter shape before this library ever shipped the first form. The code carries only the 10.3 object, EphemeralMessageParameters, and never the intermediate receiver_user_id / callback_query_id pair.

Five new methods: editEphemeralMessageText, editEphemeralMessageMedia, editEphemeralMessageCaption, editEphemeralMessageReplyMarkup, deleteEphemeralMessage.

Fourteen send* methods accept ephemeralMessageParameters. Seven others (sendMediaGroup, sendPoll, sendDice, sendChecklist, sendGame, sendInvoice, sendPaidMedia) do not, matching the documentation.

Message gains receiver_user and ephemeral_message_id. BotCommand gains is_ephemeral. ReplyParameters gains ephemeral_message_id and its message_id becomes optional.

10.2 also rewrote descriptions on fields it did not otherwise touch. Those rewrites appear in no changelog entry. Six of them changed meaning and are now reflected: Message.message_id reads 0 for ephemeral messages, Message.reply_to_message may be omitted, three ReplyParameters fields gained ephemeral caveats, and sendLocation.live_period must be 0 for ephemeral messages.

Communities

Community, CommunityChatAdded, CommunityChatRemoved and CommunityChatJoined, with the three matching service-message fields on Message and community on ChatFullInfo.

Updates

Two new update types with handlers: subscription (BotSubscriptionUpdated) and stopped_message_generation (MessageGenerationStopped). Both appear in Update::UPDATE_TYPES and both have an Event subclass.

Reply markup and the rest

DisabledButton plus disabled on InlineKeyboardButton. force_reply on InlineKeyboardMarkup and ReplyKeyboardMarkup. can_send_welcome_messages on ChatAdministratorRights, ChatMemberAdministrator and promoteChatMember. can_stop and keep_on_stop on both draft methods. text, entities and is_private on UniqueGiftInfo.

Behaviour change in BotApi

BotApi::call() used to find InputFile instances through a closure that checked four fixed property names, plus a special case for the media array of sendMediaGroup and sendPaidMedia. Rich blocks nest to arbitrary depth and carry InputMedia objects inside them, so that scan cannot reach them.

The closure now walks the parameter graph: it collects an InputFile, recurses into an array, and recurses through the properties of a Type. Both special cases went away.

This also fixes uploads that never worked. InputSticker, InputProfilePhotoStatic, InputProfilePhotoAnimated, InputStoryContentPhoto and InputStoryContentVideo matched neither old branch, so passing a local file to createNewStickerSet, addStickerToSet, replaceStickerInSet, postStory, editStory, setMyProfilePhoto or setBusinessAccountProfilePhoto produced JSON referencing attach://<name> with no matching multipart part. Telegram rejected those calls. They work now, and tests/BotApiFileUploadTest.php guards the sticker case.

One InputFile instance referenced from two places in the same request now streams once. The old code opened the file twice and dropped the first handle.

Compatibility

All new constructor parameters go at the end, including where the documentation places them earlier. ephemeralMessageParameters sits fifth in the sendMessage documentation and last in the code. UniqueGiftInfo gets its three new fields appended rather than inserted after origin. Serialization runs by name, so the wire format matches either way, and positional calls keep working. tests/EphemeralMessageTest.php pins this.

ReplyParameters::$messageId changes from required int to int|null with a default. Callers passing it stay valid.

ChatMemberAdministrator places can_send_welcome_messages before custom_title, as documented. Its constructor is protected and hydration maps by name, so no caller is affected.

Widening three constructors from protected to public cannot break a caller.

Tests

114 tests and 539 assertions, up from 26 and 304. I changed no existing test and no existing fixture: git diff -- tests/ against the base commit lists new files only.

Four areas the five-check gate cannot see:

  • tests/BotApiFileUploadTest.php parses the multipart body and asserts every attach://<name> reference has a part of that name, plus the GET/POST switch and the request URL.
  • A data provider compares all 24 outgoing block TYPE constants against their incoming twins. A mistyped discriminator passes every static check and fails only against Telegram.
  • Both new event handlers get a negative case. A checker rewritten to return true would hijack unrelated updates and pass a positive-only test.
  • A reflection test compares Update::UPDATE_TYPES against the constructor properties. A missing entry drops that update type for anyone calling setWebhook(allowedUpdates:), and nothing reports it.

The five PSR-7/17/18 doubles in tests/Helper/ add no dependency to composer.json.

Not implemented

10.2 hardened Mini App security by rejecting calls from origins other than the Mini App domain. Telegram enforces this server side and exposes the opt-out through @Botfather. No client code applies.

10.3 announced tg://document?id= links for general file uploads in rich messages. InputRichMessage.media and InputRichMessageMedia already cover the mechanism.

Pre-existing issues left alone

Three divergences predate this change and stay untouched:

  • UniqueGiftInfo::$lastResaleCurrency and $lastResaleAmount say "toncoins" and "nanotoncoins" where Telegram now says "TON grams" and "nanograms".
  • InlineKeyboardButton::$copyText has a stray space in its union type.

Verification

composer validate --no-check-lock --strict
composer dump-autoload --dry-run --optimize --strict-psr --strict-ambiguous
vendor/bin/php-cs-fixer fix -v --dry-run
vendor/bin/psalm --no-cache
vendor/bin/phpunit

All five pass on PHP 8.2.33 with Psalm 6.17.2, PHP CS Fixer 3.95.25 and PHPUnit 10.5.64.

@luzrain

luzrain commented Sep 23, 2026 •

Copy link
Copy Markdown
Owner

Looks solid. Thanks! I left a few minor comments.

@Tairesh

Tairesh commented Sep 24, 2026

Copy link
Copy Markdown
Author

Hi, unfortunately I don't see any comments right now. i will be glad to fix any issues.

Comment thread README.md Outdated
Comment thread tests/MessageGenerationStoppedTest.php Outdated
Comment thread tests/MessageGenerationStoppedTest.php Outdated
@luzrain

luzrain commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Check now. Thanks.

- Remove reflection-based parameter position/name/type tests
- Remove positional-argument construction tests
- Remove nested InputFile list from README
@Tairesh

Tairesh commented Sep 24, 2026

Copy link
Copy Markdown
Author

Fixed

@luzrain
luzrain merged commit 95abd69 into luzrain:master Sep 24, 2026
1 check passed
@luzrain

luzrain commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Thank you!

@Tairesh
Tairesh deleted the api-10.3 branch September 24, 2026 11:00
@luzrain

luzrain commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Released in v3.18.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants