From c520454ecd281d972c9a420087bc8c9614ddc9ad Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:43:44 +0300 Subject: [PATCH 01/37] ai --- .claude/skills/bot-api-update/SKILL.md | 199 +++++++++++++ .../scripts/telegram_page_to_md.py | 100 +++++++ AGENTS.md | 272 ++++++++++++++++++ 3 files changed, 571 insertions(+) create mode 100644 .claude/skills/bot-api-update/SKILL.md create mode 100644 .claude/skills/bot-api-update/scripts/telegram_page_to_md.py diff --git a/.claude/skills/bot-api-update/SKILL.md b/.claude/skills/bot-api-update/SKILL.md new file mode 100644 index 00000000..57bc6c62 --- /dev/null +++ b/.claude/skills/bot-api-update/SKILL.md @@ -0,0 +1,199 @@ +--- +name: bot-api-update +description: Implement support for a new Telegram Bot API version in this package. Use when asked to update the library to a new Bot API version. +--- + +# Telegram Bot API version update + +Port a new Telegram Bot API release into this package, end to end. + +Read `AGENTS.md` before writing any code — it holds the layout, class shapes, test patterns and +changelog rules the resulting diff is judged by. When in doubt, open a class added by the previous +Bot API commit (`git log --oneline --grep='Telegram Bot API'`) and copy its shape. + +Copy the *shape* only, never the location: older commits still put new classes into the legacy +subdirectories (`src/Method/UpdatingMessage/DeleteEphemeralMessage.php`, +`src/Type/Payment/BotSubscriptionUpdated.php`). Where a file goes is decided by `AGENTS.md` alone — +every new type lands in `src/Type`, every new method in `src/Method`. + +## 1. Repository state + +```bash +git status --porcelain # must be empty +git branch --show-current +git fetch origin +``` + +A dirty working tree stops the skill: report it and ask the user what to do. + +If the current branch is already a Bot API branch (`apiXY`) started from the current +`origin/master` tip, stay on it and continue from wherever it left off — do not switch, do not +create a new branch, and skip to the first unfinished item of `runtime/bot-api-update/plan.md`. + +Otherwise take the fresh `master` tip; the branch itself is created in step 3, once the target +version is known: + +```bash +git switch master +git pull --ff-only origin master +``` + +## 2. Download the official pages + +Everything this skill downloads or generates lives in `runtime/bot-api-update/` — `runtime/` is +fully gitignored, and the own subdirectory keeps these files apart from the other tools' output. + +```bash +mkdir -p runtime/bot-api-update +curl -fsS -o runtime/bot-api-update/api-changelog.html https://core.telegram.org/bots/api-changelog +curl -fsS -o runtime/bot-api-update/api.html https://core.telegram.org/bots/api +python3 .claude/skills/bot-api-update/scripts/telegram_page_to_md.py \ + runtime/bot-api-update/api-changelog.html runtime/bot-api-update/api-changelog.md +python3 .claude/skills/bot-api-update/scripts/telegram_page_to_md.py \ + runtime/bot-api-update/api.html runtime/bot-api-update/api.md +``` + +`api-changelog.md` says *what* changed; `api.md` (the full reference, several thousand lines — grep +it, never read it whole) gives the exact field names, types and optionality needed to write the +classes. Each type and method is a `#### ` heading followed by one `| field | type | +description` line per field, so `sed -n '/^#### Foo$/,/^#### /p'` prints exactly one definition. + +## 3. Is anything actually missing? + +- Newest released version: the first `#### ` block in + `runtime/bot-api-update/api-changelog.md`, whose first line is `**Bot API X.Y**`. +- Version the package supports: the line in `README.md` that reads + `✔️ Telegram Bot API X.Y () is **fully supported**.` + +If they match, the work is already done: tell the user that the package is up to date with +Bot API X.Y and **stop** — no branch, no plan, no commits. + +If the package is behind by more than one release, cover every missing release in this run, +oldest first, in one plan. + +Otherwise create the branch, named like the previous ones (`api102` for 10.2, `api10` for 10.0, +`api96` for 9.6 — `api` + the version digits without the dot): + +```bash +git switch -c api103 +``` + +## 4. Write the plan + +Put the plan in `runtime/bot-api-update/plan.md` (gitignored — it is a working document, never +committed). + +Split it as finely as it can reasonably be split. Rules for slicing: + +- One new type class = one item. One new method class = one item. +- One new field on an existing type = one item; several new fields on the *same* type coming from + the same changelog bullet may share one item. +- One changelog bullet that adds the same parameter to a dozen methods = one item (it is one + logical change and one `CHANGELOG.md` line). +- A new interface plus its `ValueProcessor` and `ObjectFactory` registration = one item; each + implementation of that interface is its own item. +- Every item covers its own tests. Tests are never a separate trailing item. +- Order items so dependencies come first (types before the methods that use them). + +Close the plan with these fixed items: + +1. Update the supported-version line in `README.md`. +2. Add the `CHANGELOG.md` entries. +3. `composer cs-fix` + `composer rector` (step 7). +4. Verification (step 8). + +Item template: + +```markdown +## Bot API 10.3 + +- [ ] 1. Add `EphemeralMessageParameters` type + Changelog: Added the class EphemeralMessageParameters ... + Files: src/Type/EphemeralMessageParameters.php, tests/Type/EphemeralMessageParametersTest.php + Changelog entry: New: Add `EphemeralMessageParameters` type. +``` + +Cross-check the finished plan against the changelog bullets: every bullet of the target version +must be traceable to at least one item, including "Supported ...", "Replaced ..." and +"Renamed ..." bullets, which are easy to miss. + +The `CHANGELOG.md` entries reference a PR that does not exist yet: this skill never pushes the +branch and never opens a pull request — the user does that themselves once the work is reviewed. +Only the *number* is needed here, and it is the next free issue/PR number: + +```bash +gh issue list --state all --limit 1 --json number +gh pr list --state all --limit 1 --json number +``` + +Take the larger number, add 1, and confirm that number with the user in step 5. + +## 5. Agree the plan with the user + +Show the user the plan (summary plus the item list) and the PR number you intend to use, and ask +for confirmation or corrections. Do not start implementing before they answer. Apply whatever they +change to `runtime/bot-api-update/plan.md` first. + +## 6. Implement, item by item + +For each item, in order: + +1. Implement the source change following `AGENTS.md` (backward compatibility is preserved; new + constructor parameters go last with a default). +2. Write or extend the tests in the same step. +3. Run the affected tests: `vendor/bin/phpunit --filter=`. +4. Commit only the files of this item: + ```bash + git add + git commit -m "Add EphemeralMessageParameters type" + ``` + English, imperative, one line — the PR is squashed, so these are working notes. +5. Mark the item `[x]` in `runtime/bot-api-update/plan.md`. + +Never `git add -A` — `runtime/` is ignored, but stray files are not worth the risk. Never commit +anything under `runtime/`. + +If an item turns out to be wrong or to need splitting, update `runtime/bot-api-update/plan.md` and +tell the user what changed, then continue. + +## 7. cs-fix and rector + +```bash +composer cs-fix +composer rector +composer test +``` + +Review the diff these tools produce before committing it — rector occasionally rewrites something +in a way that conflicts with the project style. Commit as `Apply cs-fix and rector`. + +## 8. Verification + +Report all four results to the user, honestly, with the actual output of anything that fails. + +1. **Completeness.** Walk the changelog bullets of the target version one by one and confirm each + is implemented in `src/`. Confirm every `[ ]` in `runtime/bot-api-update/plan.md` is now + `[x]`. +2. **Tests.** + ```bash + composer test + ``` +3. **Coverage — must be 100%.** + ```bash + XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-text # or `composer coverage` for HTML + ``` + If PHPUnit warns `No code coverage driver available` (the usual case in this environment — + neither Xdebug nor PCOV is installed), say so plainly instead of claiming coverage. Then fall + back to a manual check: every class added or changed in this branch + (`git diff --stat master...HEAD -- src`) has a corresponding test that exercises the new code, + including every new facade method in `src/TelegramBotApi.php`. Tell the user that CI + (Coveralls) gives the authoritative number. +4. **Static analysis — must be clean.** + ```bash + composer psalm + composer dependency-analyser + ``` + +Finish with a short summary: version ported, number of commits, what each verification +step returned, and anything left open. Leave the branch local and unpushed — say that pushing it +and opening the PR is up to the user. diff --git a/.claude/skills/bot-api-update/scripts/telegram_page_to_md.py b/.claude/skills/bot-api-update/scripts/telegram_page_to_md.py new file mode 100644 index 00000000..f422408e --- /dev/null +++ b/.claude/skills/bot-api-update/scripts/telegram_page_to_md.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +"""Convert a saved core.telegram.org page into readable Markdown. + +Usage: + python3 telegram_page_to_md.py [output.md] + +Works both for https://core.telegram.org/bots/api-changelog and for +https://core.telegram.org/bots/api (the full reference is huge, so the +result is meant to be searched with grep, not read as a whole). +""" + +from __future__ import annotations + +import html +import re +import sys + +BLOCK_END = re.compile(r"", re.IGNORECASE) +TABLE_ROW = re.compile(r"]*>(.*?)", re.IGNORECASE | re.DOTALL) +TABLE_CELL = re.compile(r"]*>(.*?)", re.IGNORECASE | re.DOTALL) +TAG = re.compile(r"<[^>]+>") + + +def _clean_cell(cell: str) -> str: + return " ".join(TAG.sub("", cell).split()) + + +def _convert_row(match: re.Match[str]) -> str: + cells = [_clean_cell(cell) for cell in TABLE_CELL.findall(match.group(1))] + if not cells: + return "\n" + return "\n| " + " | ".join(cells) + " |\n" + + +def convert(source: str) -> str: + marker = '
", "", body, flags=re.IGNORECASE | re.DOTALL) + body = re.sub(r"", "", body, flags=re.IGNORECASE | re.DOTALL) + + # Anchor icons would otherwise turn into stray emphasis markers. + body = re.sub(r"", "", body, flags=re.IGNORECASE) + + # Keep inline emphasis: Telegram marks field and parameter names with . + body = re.sub(r"", "**", body, flags=re.IGNORECASE) + body = re.sub(r"", "_", body, flags=re.IGNORECASE) + body = re.sub(r"", "`", body, flags=re.IGNORECASE) + + # A table row becomes a single line, so that one field is one grep hit. + body = TABLE_ROW.sub(_convert_row, body) + + for level in range(1, 7): + body = re.sub(rf"]*>", "\n\n" + "#" * level + " ", body, flags=re.IGNORECASE) + body = re.sub(r"]*>", "\n- ", body, flags=re.IGNORECASE) + body = re.sub(r"", "", body, flags=re.IGNORECASE) + body = BLOCK_END.sub("\n", body) + body = re.sub(r"", "\n", body, flags=re.IGNORECASE) + + text = html.unescape(TAG.sub("", body)) + # The anchor icons leave empty emphasis markers behind. + text = text.replace("____", "").replace("****", "") + + lines = [line.strip() for line in text.splitlines()] + result: list[str] = [] + for index, line in enumerate(lines): + if not line: + if not result or not result[-1]: + continue + # Keep list items and table rows packed together. + following = next((item for item in lines[index + 1:] if item), "") + for prefix in ("- ", "|"): + if result[-1].startswith(prefix) and following.startswith(prefix): + break + else: + result.append(line) + continue + result.append(line) + return "\n".join(result).strip() + "\n" + + +def main() -> int: + if len(sys.argv) < 2: + print(__doc__, file=sys.stderr) + return 1 + + with open(sys.argv[1], encoding="utf-8") as handle: + text = convert(handle.read()) + + if len(sys.argv) > 2: + with open(sys.argv[2], "w", encoding="utf-8") as handle: + handle.write(text) + else: + sys.stdout.write(text) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/AGENTS.md b/AGENTS.md index f4ebe549..d762c155 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,10 +1,282 @@ # Agent Guidelines +## Project Layout + +| Bot API concept | File | +| --- | --- | +| Type | `src/Type/.php` (`Phptg\BotApi\Type`) | +| Method | `src/Method/.php` (`Phptg\BotApi\Method`) | +| Facade shortcut | a method on `src/TelegramBotApi.php` | +| Union/interface dispatch | `src/ParseResult/ValueProcessor/Value.php` — see *Interfaces and Unions* | + +The subdirectories under `src/Type` and `src/Method` are legacy. Never put a new class there — every new type +and method goes directly into `src/Type` and `src/Method`. + +Tests mirror the source path: `src/Type/Foo.php` → `tests/Type/FooTest.php`. + +## Result Types + +Objects the API returns: + +```php + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + return [ + 'type' => $this->getType(), + 'text' => RichTextConverter::toRequestArray($this->text), + ]; + } +} +``` + +- Drop `null` values from the produced array the same way the neighbouring class does. +- A type that can carry an uploaded file takes the `?FileCollector $fileCollector = null` argument + and registers the file through it. + +## Interfaces and Unions + +A Bot API "can be one of" type becomes an interface in `src/Type`, usually declaring `getType()`, +with `@see` and `@api` in the doc block — for example `src/Type/RichBlock.php`. + +A value processor is needed only for unions the API **returns**. Types that are only sent +(`InputRichBlock`, `InputMedia`, `InputPaidMedia`, `InputProfilePhoto`, …) have no processor at all: +they are serialized by `toRequestArray()`. + +For a returned union add +`src/ParseResult/ValueProcessor/RichBlockValue.php` — `final readonly class ... extends +InterfaceValue`, with `@template-extends InterfaceValue` and three methods: + +- `getTypeKey()` — the discriminator field, usually `'type'`; +- `getClassMap()` — discriminator value → class; +- `getUnknownTypeMessage()` — the message of the exception thrown for an unknown discriminator, + e.g. `'Unknown rich block type.'`. + +All three are abstract in `InterfaceValue`; forgetting the last one leaves the class abstract. + +Then wire the processor up. There are two ways, and they are not interchangeable: + +- The interface is the declared type of a property — register the processor in + `ObjectFactory::getTypeMap()`: `RichBlock::class => new RichBlockValue(),`. This covers every + property of that type at once. +- The interface appears inside an array, or the property needs a processor the type alone cannot + imply — put the processor on the property as an attribute: + `#[ArrayMap(PaidMediaValue::class)]` in `PaidMediaInfo`, `#[MaybeInaccessibleMessageValue]` in + `Message`. Such processors are *not* in `getTypeMap()`. + +Every implementation must be added to `getClassMap()`, otherwise parsing silently fails. + +## Methods + +```php +final readonly class DeleteEphemeralMessage implements MethodInterface +{ + public function __construct( + private int|string $chatId, + private int $receiverUserId, + private int $ephemeralMessageId, + ) {} + + public function getHttpMethod(): HttpMethod + { + return HttpMethod::POST; + } + + public function getApiMethod(): string + { + return 'deleteEphemeralMessage'; + } + + public function getData(): array + { + return [ + 'chat_id' => $this->chatId, + 'receiver_user_id' => $this->receiverUserId, + 'ephemeral_message_id' => $this->ephemeralMessageId, + ]; + } + + public function getResultType(): TrueValue + { + return new TrueValue(); + } +} +``` + +- `final readonly`, promoted **private** properties, `@see` link, and + `@template-implements MethodInterface` in the doc block (`` for methods returning + `True`). +- `getData()` returns snake_case keys. When optional parameters exist, wrap the array in + `array_filter(..., static fn(mixed $value): bool => $value !== null)`. +- Nested objects are serialized with `?->toRequestArray()`; arrays of objects with `array_map`. +- Result processors: `TrueValue`, `ObjectValue(Message::class)`, `ArrayOfObjectsValue(...)`, + `StringValue`, `ObjectOrTrueValue`, … — pick the one used by the closest existing method. + +## Facade + +Every method class gets a shortcut on `src/TelegramBotApi.php`. The list is alphabetical — insert +the new method next to its alphabetical neighbours, and add the matching `use` import: + +```php +/** + * @see https://core.telegram.org/bots/api#deleteephemeralmessage + */ +public function deleteEphemeralMessage( + int|string $chatId, + int $receiverUserId, + int $ephemeralMessageId, +): FailResult|true { + return $this->call(new DeleteEphemeralMessage($chatId, $receiverUserId, $ephemeralMessageId)); +} +``` + +The return type is `FailResult|`. + ## Writing Tests - For `InputFile|string` parameters, the string must be a file ID, not a URL. +- Assertions are the imported `PHPUnit\Framework\assert*` functions, not `$this->assert*`. +- Test case names are fixed. `testBase()` is always the minimal object — required arguments only — + and `testFull()` is the same object with **every** optional argument passed. Do not invent other + names; a handful of old tests use `testFilled()` or `testFromTelegramResultMinimal()`, they are + not the convention. +- Result types — `tests/Type/Test.php`. Without optional fields, two cases: + ```php + final class CommunityTest extends TestCase + { + public function testBase(): void + { + $community = new Community(123, 'My Community'); + + assertSame(123, $community->id); + assertSame('My Community', $community->name); + } + + public function testFromTelegramResult(): void + { + $community = (new ObjectFactory())->create([ + 'id' => 123, + 'name' => 'My Community', + ], null, Community::class); + + assertSame(123, $community->id); + assertSame('My Community', $community->name); + } + } + ``` + With optional fields, four: + + | Case | What it does | + | --- | --- | + | `testBase()` | required arguments only; asserts every optional field is `null` | + | `testFull()` | all optional arguments passed; asserts each of them | + | `testFromTelegramResult()` | `ObjectFactory` on the minimal payload; asserts the optional fields are `null` | + | `testFromTelegramResultFull()` | `ObjectFactory` on the payload with every key present | + + In the `FromTelegramResult` cases nested objects are checked with `assertInstanceOf()` plus a + couple of their fields — see `tests/Type/RichBlockAnimationTest.php`. +- Input types — same `testBase()`/`testFull()` split, but each case asserts the exact + `toRequestArray()` array instead of the properties. A type that accepts an `InputFile` gets one + more case, `testFileCollectorIsPropagated()`, which passes a `FileCollector`, expects + `attach://file0` in the array and asserts `$fileCollector->get()`. +- Methods — `tests/Method/Test.php`: `testBase()` asserts `getHttpMethod()`, `getApiMethod()` + and the exact `getData()` array; `testFull()` repeats it with every optional parameter passed + (keys in `getData()` order, not in constructor order); `testPrepareResult()` runs the method + through `TestHelper::createSuccessStubApi(...)`. +- Facade — one `test()` in `tests/TelegramBotApi/TelegramBotApiTest.php`, alphabetically + placed, using `TestHelper::createSuccessStubApi(...)`: build the stub, call the method, assert the + result. Optional parameters are not repeated here — they are already covered by the method test. +- New fields on existing types extend the existing test of that type rather than adding a new file: + the new field goes into `testFull()` and `testFromTelegramResultFull()`, and into `testBase()` / + `testFromTelegramResult()` as a `null` assertion. ## Preserving Backward Compatibility - Backward compatibility should be preserved as much as possible. - A new parameter is always added last (with a default value), regardless of its position in the Bot API docs. +- The same holds for a new field on an existing type: it goes after the existing constructor + parameters, even though `getData()` still lists it in its Bot API position. + +## CHANGELOG + +Entries go under the existing `## X.Y.Z under development` heading (create one if the top section +is already released). Format: `- #: `, wrapped at 120 characters, continuation +lines indented by two spaces. + +- `New` — new types, methods, fields, parameters. +- `Chg` — behaviour or signature changes. +- `Enh` — improvements to existing code. +- `Bug` — fixes. + +Group related additions into a single entry: + +```markdown +- New #211: Add `InputMediaVoiceNote`, `InputRichMessageMedia`, `Community` and + `CommunityChatAdded` types. +- New #211: Add `receiverUserId` and `callbackQueryId` parameters to `SendMessage`, `SendAnimation` + and `SendAudio` methods. +- Chg #211: Make `messageId` field of `ReplyParameters` type optional. +``` + +Class, field and parameter names in the changelog are the **PHP** names (camelCase), not the Bot +API ones. From 2118ffbee491e4998e0918f1ac9307c82fb07d57 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:51:24 +0300 Subject: [PATCH 02/37] Add DisabledButton type --- src/Type/DisabledButton.php | 18 ++++++++++++++++++ tests/Type/DisabledButtonTest.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/Type/DisabledButton.php create mode 100644 tests/Type/DisabledButtonTest.php diff --git a/src/Type/DisabledButton.php b/src/Type/DisabledButton.php new file mode 100644 index 00000000..d1505b12 --- /dev/null +++ b/src/Type/DisabledButton.php @@ -0,0 +1,18 @@ +toRequestArray()); + } + + public function testFromTelegramResult(): void + { + $button = (new ObjectFactory())->create([], null, DisabledButton::class); + + assertInstanceOf(DisabledButton::class, $button); + } +} From 17ccf757404eca04f7be3e50a1369ea968de0e44 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:51:48 +0300 Subject: [PATCH 03/37] Add disabled field to InlineKeyboardButton type --- src/Type/InlineKeyboardButton.php | 2 ++ tests/Type/InlineKeyboardButtonTest.php | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/src/Type/InlineKeyboardButton.php b/src/Type/InlineKeyboardButton.php index 8bbc9d2b..50d5b5cd 100644 --- a/src/Type/InlineKeyboardButton.php +++ b/src/Type/InlineKeyboardButton.php @@ -27,6 +27,7 @@ public function __construct( public ?CopyTextButton $copyText = null, public ?string $iconCustomEmojiId = null, public ?string $style = null, + public ?DisabledButton $disabled = null, ) {} public function toRequestArray(): array @@ -46,6 +47,7 @@ public function toRequestArray(): array 'copy_text' => $this->copyText?->toRequestArray(), 'callback_game' => $this->callbackGame?->toRequestArray(), 'pay' => $this->pay, + 'disabled' => $this->disabled?->toRequestArray(), ], static fn(mixed $value): bool => $value !== null, ); diff --git a/tests/Type/InlineKeyboardButtonTest.php b/tests/Type/InlineKeyboardButtonTest.php index fee9733b..33cb1f24 100644 --- a/tests/Type/InlineKeyboardButtonTest.php +++ b/tests/Type/InlineKeyboardButtonTest.php @@ -7,6 +7,7 @@ use PHPUnit\Framework\TestCase; use Phptg\BotApi\ParseResult\ObjectFactory; use Phptg\BotApi\Type\CopyTextButton; +use Phptg\BotApi\Type\DisabledButton; use Phptg\BotApi\Type\Game\CallbackGame; use Phptg\BotApi\Type\InlineKeyboardButton; use Phptg\BotApi\Type\LoginUrl; @@ -37,6 +38,7 @@ public function testBase(): void assertNull($button->copyText); assertNull($button->callbackGame); assertNull($button->pay); + assertNull($button->disabled); assertSame( [ @@ -53,6 +55,7 @@ public function testFilled(): void $switchInlineQueryChosenChat = new SwitchInlineQueryChosenChat('dg'); $callbackGame = new CallbackGame(); $copyText = new CopyTextButton('Copy it!'); + $disabled = new DisabledButton(); $button = new InlineKeyboardButton( 'test', 'https://example.com', @@ -67,6 +70,7 @@ public function testFilled(): void $copyText, '5368324170671202286', 'primary', + $disabled, ); assertSame('test', $button->text); @@ -82,6 +86,7 @@ public function testFilled(): void assertSame($copyText, $button->copyText); assertSame($callbackGame, $button->callbackGame); assertTrue($button->pay); + assertSame($disabled, $button->disabled); assertSame( [ @@ -98,6 +103,7 @@ public function testFilled(): void 'copy_text' => $copyText->toRequestArray(), 'callback_game' => $callbackGame->toRequestArray(), 'pay' => true, + 'disabled' => [], ], $button->toRequestArray(), ); @@ -119,6 +125,7 @@ public function testFromTelegramResult(): void 'copy_text' => ['text' => 'Copy it!'], 'callback_game' => [], 'pay' => true, + 'disabled' => [], ], null, InlineKeyboardButton::class); assertSame('test', $button->text); @@ -134,5 +141,6 @@ public function testFromTelegramResult(): void assertSame('Copy it!', $button->copyText->text); assertInstanceOf(CallbackGame::class, $button->callbackGame); assertTrue($button->pay); + assertInstanceOf(DisabledButton::class, $button->disabled); } } From 33e855c9b61e2c7b680a0a61a9c3add401755d22 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:52:15 +0300 Subject: [PATCH 04/37] Add forceReply field to InlineKeyboardMarkup type --- src/Type/InlineKeyboardMarkup.php | 21 +++++++---- tests/Type/InlineKeyboardMarkupTest.php | 50 +++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/Type/InlineKeyboardMarkup.php b/src/Type/InlineKeyboardMarkup.php index e5f56467..198da97d 100644 --- a/src/Type/InlineKeyboardMarkup.php +++ b/src/Type/InlineKeyboardMarkup.php @@ -20,18 +20,23 @@ public function __construct( #[ArrayOfArraysOfObjectsValue(InlineKeyboardButton::class)] public array $inlineKeyboard, + public ?bool $forceReply = null, ) {} public function toRequestArray(): array { - return [ - 'inline_keyboard' => array_map( - static fn(array $buttons) => array_map( - static fn(InlineKeyboardButton $button) => $button->toRequestArray(), - $buttons, + return array_filter( + [ + 'inline_keyboard' => array_map( + static fn(array $buttons) => array_map( + static fn(InlineKeyboardButton $button) => $button->toRequestArray(), + $buttons, + ), + $this->inlineKeyboard, ), - $this->inlineKeyboard, - ), - ]; + 'force_reply' => $this->forceReply, + ], + static fn(mixed $value): bool => $value !== null, + ); } } diff --git a/tests/Type/InlineKeyboardMarkupTest.php b/tests/Type/InlineKeyboardMarkupTest.php index 8d1fba70..d3bac07b 100644 --- a/tests/Type/InlineKeyboardMarkupTest.php +++ b/tests/Type/InlineKeyboardMarkupTest.php @@ -11,7 +11,9 @@ use function PHPUnit\Framework\assertCount; use function PHPUnit\Framework\assertInstanceOf; +use function PHPUnit\Framework\assertNull; use function PHPUnit\Framework\assertSame; +use function PHPUnit\Framework\assertTrue; final class InlineKeyboardMarkupTest extends TestCase { @@ -23,6 +25,7 @@ public function testBase(): void ]); assertSame([[$button]], $markup->inlineKeyboard); + assertNull($markup->forceReply); assertSame( [ @@ -36,6 +39,32 @@ public function testBase(): void ); } + public function testFull(): void + { + $button = new InlineKeyboardButton('test'); + $markup = new InlineKeyboardMarkup( + [ + [$button], + ], + true, + ); + + assertSame([[$button]], $markup->inlineKeyboard); + assertTrue($markup->forceReply); + + assertSame( + [ + 'inline_keyboard' => [ + [ + $button->toRequestArray(), + ], + ], + 'force_reply' => true, + ], + $markup->toRequestArray(), + ); + } + public function testFromTelegramResult(): void { $markup = (new ObjectFactory())->create([ @@ -52,5 +81,26 @@ public function testFromTelegramResult(): void assertCount(1, $markup->inlineKeyboard[0]); assertInstanceOf(InlineKeyboardButton::class, $markup->inlineKeyboard[0][0]); assertSame('test', $markup->inlineKeyboard[0][0]->text); + assertNull($markup->forceReply); + } + + public function testFromTelegramResultFull(): void + { + $markup = (new ObjectFactory())->create([ + 'inline_keyboard' => [ + [ + [ + 'text' => 'test', + ], + ], + ], + 'force_reply' => true, + ], null, InlineKeyboardMarkup::class); + + assertCount(1, $markup->inlineKeyboard); + assertCount(1, $markup->inlineKeyboard[0]); + assertInstanceOf(InlineKeyboardButton::class, $markup->inlineKeyboard[0][0]); + assertSame('test', $markup->inlineKeyboard[0][0]->text); + assertTrue($markup->forceReply); } } From 8358773c285932742cb31444bc879f4eaa5be5dc Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:52:27 +0300 Subject: [PATCH 05/37] Add forceReply field to ReplyKeyboardMarkup type --- src/Type/ReplyKeyboardMarkup.php | 2 ++ tests/Type/ReplyKeyboardMarkupTest.php | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Type/ReplyKeyboardMarkup.php b/src/Type/ReplyKeyboardMarkup.php index 394d6667..c7a967ca 100644 --- a/src/Type/ReplyKeyboardMarkup.php +++ b/src/Type/ReplyKeyboardMarkup.php @@ -22,6 +22,7 @@ public function __construct( public ?bool $oneTimeKeyboard = null, public ?string $inputFieldPlaceholder = null, public ?bool $selective = null, + public ?bool $forceReply = null, ) {} public function toRequestArray(): array @@ -40,6 +41,7 @@ public function toRequestArray(): array 'one_time_keyboard' => $this->oneTimeKeyboard, 'input_field_placeholder' => $this->inputFieldPlaceholder, 'selective' => $this->selective, + 'force_reply' => $this->forceReply, ], static fn(mixed $value): bool => $value !== null, ); diff --git a/tests/Type/ReplyKeyboardMarkupTest.php b/tests/Type/ReplyKeyboardMarkupTest.php index 7b834ba2..55e409c4 100644 --- a/tests/Type/ReplyKeyboardMarkupTest.php +++ b/tests/Type/ReplyKeyboardMarkupTest.php @@ -26,6 +26,7 @@ public function testBase(): void assertNull($markup->oneTimeKeyboard); assertNull($markup->inputFieldPlaceholder); assertNull($markup->selective); + assertNull($markup->forceReply); assertSame( [ @@ -38,7 +39,7 @@ public function testBase(): void public function testFilled(): void { $button = new KeyboardButton('text'); - $markup = new ReplyKeyboardMarkup([[$button]], true, false, true, 'test', true); + $markup = new ReplyKeyboardMarkup([[$button]], true, false, true, 'test', true, true); assertSame([[$button]], $markup->keyboard); assertTrue($markup->isPersistent); @@ -46,6 +47,7 @@ public function testFilled(): void assertTrue($markup->oneTimeKeyboard); assertSame('test', $markup->inputFieldPlaceholder); assertTrue($markup->selective); + assertTrue($markup->forceReply); assertSame( [ @@ -55,6 +57,7 @@ public function testFilled(): void 'one_time_keyboard' => true, 'input_field_placeholder' => 'test', 'selective' => true, + 'force_reply' => true, ], $markup->toRequestArray(), ); From 298c24f30424e34ba2df46d9fc0bfe6db3abb658 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:53:11 +0300 Subject: [PATCH 06/37] Add RichMessageButton type --- src/Type/RichMessageButton.php | 54 ++++++++++ tests/Type/RichMessageButtonTest.php | 154 +++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 src/Type/RichMessageButton.php create mode 100644 tests/Type/RichMessageButtonTest.php diff --git a/src/Type/RichMessageButton.php b/src/Type/RichMessageButton.php new file mode 100644 index 00000000..d30c5575 --- /dev/null +++ b/src/Type/RichMessageButton.php @@ -0,0 +1,54 @@ + + */ + public function toRequestArray(): array + { + return array_filter( + [ + 'text' => RichTextConverter::toRequestArray($this->text), + 'style' => $this->style, + 'url' => $this->url, + 'callback_data' => $this->callbackData, + 'web_app' => $this->webApp?->toRequestArray(), + 'login_url' => $this->loginUrl?->toRequestArray(), + 'switch_inline_query' => $this->switchInlineQuery, + 'switch_inline_query_current_chat' => $this->switchInlineQueryCurrentChat, + 'switch_inline_query_chosen_chat' => $this->switchInlineQueryChosenChat?->toRequestArray(), + 'copy_text' => $this->copyText?->toRequestArray(), + 'disabled' => $this->disabled?->toRequestArray(), + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/tests/Type/RichMessageButtonTest.php b/tests/Type/RichMessageButtonTest.php new file mode 100644 index 00000000..d6bd56ef --- /dev/null +++ b/tests/Type/RichMessageButtonTest.php @@ -0,0 +1,154 @@ +text); + assertNull($button->style); + assertNull($button->url); + assertNull($button->callbackData); + assertNull($button->webApp); + assertNull($button->loginUrl); + assertNull($button->switchInlineQuery); + assertNull($button->switchInlineQueryCurrentChat); + assertNull($button->switchInlineQueryChosenChat); + assertNull($button->copyText); + assertNull($button->disabled); + + assertSame( + [ + 'text' => 'test', + ], + $button->toRequestArray(), + ); + } + + public function testFull(): void + { + $webApp = new WebAppInfo('https://example.com/test'); + $loginUrl = new LoginUrl('https://example.com/login'); + $switchInlineQueryChosenChat = new SwitchInlineQueryChosenChat('dg'); + $copyText = new CopyTextButton('Copy it!'); + $disabled = new DisabledButton(); + $text = new RichTextBold('test'); + $button = new RichMessageButton( + $text, + 'danger', + 'https://example.com', + 'callback-data', + $webApp, + $loginUrl, + 'switch-inline-query', + 'switch-inline-query-current-chat', + $switchInlineQueryChosenChat, + $copyText, + $disabled, + ); + + assertSame($text, $button->text); + assertSame('danger', $button->style); + assertSame('https://example.com', $button->url); + assertSame('callback-data', $button->callbackData); + assertSame($webApp, $button->webApp); + assertSame($loginUrl, $button->loginUrl); + assertSame('switch-inline-query', $button->switchInlineQuery); + assertSame('switch-inline-query-current-chat', $button->switchInlineQueryCurrentChat); + assertSame($switchInlineQueryChosenChat, $button->switchInlineQueryChosenChat); + assertSame($copyText, $button->copyText); + assertSame($disabled, $button->disabled); + + assertSame( + [ + 'text' => $text->toRequestArray(), + 'style' => 'danger', + 'url' => 'https://example.com', + 'callback_data' => 'callback-data', + 'web_app' => $webApp->toRequestArray(), + 'login_url' => $loginUrl->toRequestArray(), + 'switch_inline_query' => 'switch-inline-query', + 'switch_inline_query_current_chat' => 'switch-inline-query-current-chat', + 'switch_inline_query_chosen_chat' => $switchInlineQueryChosenChat->toRequestArray(), + 'copy_text' => $copyText->toRequestArray(), + 'disabled' => [], + ], + $button->toRequestArray(), + ); + } + + public function testFromTelegramResult(): void + { + $button = (new ObjectFactory())->create([ + 'text' => 'test', + ], null, RichMessageButton::class); + + assertSame('test', $button->text); + assertNull($button->style); + assertNull($button->url); + assertNull($button->callbackData); + assertNull($button->webApp); + assertNull($button->loginUrl); + assertNull($button->switchInlineQuery); + assertNull($button->switchInlineQueryCurrentChat); + assertNull($button->switchInlineQueryChosenChat); + assertNull($button->copyText); + assertNull($button->disabled); + } + + public function testFromTelegramResultFull(): void + { + $button = (new ObjectFactory())->create([ + 'text' => [ + 'type' => 'bold', + 'text' => 'test', + ], + 'style' => 'danger', + 'url' => 'https://example.com', + 'callback_data' => 'callback-data', + 'web_app' => ['url' => 'https://example.com/test'], + 'login_url' => ['url' => 'https://example.com/login'], + 'switch_inline_query' => 'switch-inline-query', + 'switch_inline_query_current_chat' => 'switch-inline-query-current-chat', + 'switch_inline_query_chosen_chat' => ['query' => 'dg'], + 'copy_text' => ['text' => 'Copy it!'], + 'disabled' => [], + ], null, RichMessageButton::class); + + assertInstanceOf(RichTextBold::class, $button->text); + assertSame('test', $button->text->text); + assertSame('danger', $button->style); + assertSame('https://example.com', $button->url); + assertSame('callback-data', $button->callbackData); + assertInstanceOf(WebAppInfo::class, $button->webApp); + assertSame('https://example.com/test', $button->webApp->url); + assertInstanceOf(LoginUrl::class, $button->loginUrl); + assertSame('https://example.com/login', $button->loginUrl->url); + assertSame('switch-inline-query', $button->switchInlineQuery); + assertSame('switch-inline-query-current-chat', $button->switchInlineQueryCurrentChat); + assertInstanceOf(SwitchInlineQueryChosenChat::class, $button->switchInlineQueryChosenChat); + assertSame('dg', $button->switchInlineQueryChosenChat->query); + assertInstanceOf(CopyTextButton::class, $button->copyText); + assertSame('Copy it!', $button->copyText->text); + assertInstanceOf(DisabledButton::class, $button->disabled); + } +} From 7e39f853dd540ca1d0f6788775a6681c620a0000 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:53:43 +0300 Subject: [PATCH 07/37] Add RichTextButton type --- .../ValueProcessor/RichTextValue.php | 2 + src/Type/RichTextButton.php | 30 +++++++++ tests/Type/RichTextButtonTest.php | 65 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/Type/RichTextButton.php create mode 100644 tests/Type/RichTextButtonTest.php diff --git a/src/ParseResult/ValueProcessor/RichTextValue.php b/src/ParseResult/ValueProcessor/RichTextValue.php index d20e1cb5..4842ef9b 100644 --- a/src/ParseResult/ValueProcessor/RichTextValue.php +++ b/src/ParseResult/ValueProcessor/RichTextValue.php @@ -21,6 +21,7 @@ use Phptg\BotApi\Type\RichTextReference; use Phptg\BotApi\Type\RichTextReferenceLink; use Phptg\BotApi\Type\RichTextBotCommand; +use Phptg\BotApi\Type\RichTextButton; use Phptg\BotApi\Type\RichTextCashtag; use Phptg\BotApi\Type\RichTextHashtag; use Phptg\BotApi\Type\RichTextMention; @@ -75,6 +76,7 @@ 'anchor_link' => RichTextAnchorLink::class, 'reference' => RichTextReference::class, 'reference_link' => RichTextReferenceLink::class, + 'button' => RichTextButton::class, ]; public function process(mixed $value, ?string $key, ObjectFactory $objectFactory): string|array|RichText diff --git a/src/Type/RichTextButton.php b/src/Type/RichTextButton.php new file mode 100644 index 00000000..0ac1f861 --- /dev/null +++ b/src/Type/RichTextButton.php @@ -0,0 +1,30 @@ + $this->getType(), + 'button' => $this->button->toRequestArray(), + ]; + } +} diff --git a/tests/Type/RichTextButtonTest.php b/tests/Type/RichTextButtonTest.php new file mode 100644 index 00000000..503672a2 --- /dev/null +++ b/tests/Type/RichTextButtonTest.php @@ -0,0 +1,65 @@ +getType()); + assertSame($button, $richText->button); + assertSame( + [ + 'type' => 'button', + 'button' => [ + 'text' => 'test', + 'callback_data' => 'data', + ], + ], + $richText->toRequestArray(), + ); + } + + public function testFromTelegramResult(): void + { + $richText = (new ObjectFactory())->create([ + 'type' => 'button', + 'button' => [ + 'text' => 'test', + 'callback_data' => 'data', + ], + ], null, RichTextButton::class); + + assertSame('button', $richText->getType()); + assertInstanceOf(RichMessageButton::class, $richText->button); + assertSame('test', $richText->button->text); + assertSame('data', $richText->button->callbackData); + } + + public function testFromTelegramResultViaRichTextValue(): void + { + $block = (new ObjectFactory())->create([ + 'type' => 'paragraph', + 'text' => [ + 'type' => 'button', + 'button' => ['text' => 'test'], + ], + ], null, RichBlockParagraph::class); + + assertInstanceOf(RichTextButton::class, $block->text); + } +} From e668590c18cc49cd65f1b3f9d276ac45a43d4169 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:54:08 +0300 Subject: [PATCH 08/37] Add RichBlockButtons type --- .../ValueProcessor/RichBlockValue.php | 2 + src/Type/RichBlockButtons.php | 29 ++++++++ tests/Type/RichBlockButtonsTest.php | 70 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/Type/RichBlockButtons.php create mode 100644 tests/Type/RichBlockButtonsTest.php diff --git a/src/ParseResult/ValueProcessor/RichBlockValue.php b/src/ParseResult/ValueProcessor/RichBlockValue.php index b45afe0f..b5eacbfa 100644 --- a/src/ParseResult/ValueProcessor/RichBlockValue.php +++ b/src/ParseResult/ValueProcessor/RichBlockValue.php @@ -9,6 +9,7 @@ use Phptg\BotApi\Type\RichBlockDivider; use Phptg\BotApi\Type\RichBlockAnchor; use Phptg\BotApi\Type\RichBlockBlockQuotation; +use Phptg\BotApi\Type\RichBlockButtons; use Phptg\BotApi\Type\RichBlockCollage; use Phptg\BotApi\Type\RichBlockSlideshow; use Phptg\BotApi\Type\RichBlockDetails; @@ -61,6 +62,7 @@ public function getClassMap(): array 'video' => RichBlockVideo::class, 'voice_note' => RichBlockVoiceNote::class, 'thinking' => RichBlockThinking::class, + 'buttons' => RichBlockButtons::class, ]; } diff --git a/src/Type/RichBlockButtons.php b/src/Type/RichBlockButtons.php new file mode 100644 index 00000000..827ff47d --- /dev/null +++ b/src/Type/RichBlockButtons.php @@ -0,0 +1,29 @@ + $buttons + */ + public function __construct( + #[ArrayOfObjectsValue(RichMessageButton::class)] + public array $buttons, + public ?string $align = null, + ) {} + + public function getType(): string + { + return 'buttons'; + } +} diff --git a/tests/Type/RichBlockButtonsTest.php b/tests/Type/RichBlockButtonsTest.php new file mode 100644 index 00000000..1677254b --- /dev/null +++ b/tests/Type/RichBlockButtonsTest.php @@ -0,0 +1,70 @@ +getType()); + assertSame([$button], $block->buttons); + assertNull($block->align); + } + + public function testFull(): void + { + $button = new RichMessageButton('test'); + $block = new RichBlockButtons([$button], 'center'); + + assertSame('buttons', $block->getType()); + assertSame([$button], $block->buttons); + assertSame('center', $block->align); + } + + public function testFromTelegramResult(): void + { + $block = (new ObjectFactory())->create([ + 'type' => 'buttons', + 'buttons' => [ + ['text' => 'test'], + ], + ], null, RichBlockButtons::class); + + assertSame('buttons', $block->getType()); + assertCount(1, $block->buttons); + assertContainsOnlyInstancesOf(RichMessageButton::class, $block->buttons); + assertSame('test', $block->buttons[0]->text); + assertNull($block->align); + } + + public function testFromTelegramResultFull(): void + { + $block = (new ObjectFactory())->create([ + 'type' => 'buttons', + 'buttons' => [ + ['text' => 'test'], + ], + 'align' => 'right', + ], null, RichBlockButtons::class); + + assertSame('buttons', $block->getType()); + assertCount(1, $block->buttons); + assertContainsOnlyInstancesOf(RichMessageButton::class, $block->buttons); + assertSame('right', $block->align); + } +} From cc5f6a776de297bc0c06fd9d0aa72ebb476168ca Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:54:24 +0300 Subject: [PATCH 09/37] Add InputRichBlockButtons type --- src/Type/InputRichBlockButtons.php | 46 ++++++++++++++++++++++ tests/Type/InputRichBlockButtonsTest.php | 49 ++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/Type/InputRichBlockButtons.php create mode 100644 tests/Type/InputRichBlockButtonsTest.php diff --git a/src/Type/InputRichBlockButtons.php b/src/Type/InputRichBlockButtons.php new file mode 100644 index 00000000..c22c30ff --- /dev/null +++ b/src/Type/InputRichBlockButtons.php @@ -0,0 +1,46 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + return array_filter( + [ + 'type' => $this->getType(), + 'buttons' => array_map( + static fn(RichMessageButton $button) => $button->toRequestArray(), + $this->buttons, + ), + 'align' => $this->align, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/tests/Type/InputRichBlockButtonsTest.php b/tests/Type/InputRichBlockButtonsTest.php new file mode 100644 index 00000000..ee507757 --- /dev/null +++ b/tests/Type/InputRichBlockButtonsTest.php @@ -0,0 +1,49 @@ +getType()); + assertSame( + [ + 'type' => 'buttons', + 'buttons' => [ + ['text' => 'test', 'callback_data' => 'data'], + ], + ], + $block->toRequestArray(), + ); + } + + public function testFull(): void + { + $block = new InputRichBlockButtons( + [new RichMessageButton('test', callbackData: 'data')], + 'left', + ); + + assertSame( + [ + 'type' => 'buttons', + 'buttons' => [ + ['text' => 'test', 'callback_data' => 'data'], + ], + 'align' => 'left', + ], + $block->toRequestArray(), + ); + } +} From 8d4978e22370134455169dc95bbbebc5c99dd541 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:54:46 +0300 Subject: [PATCH 10/37] Add isCompact field to RichBlockTable type --- src/Type/RichBlockTable.php | 1 + tests/Type/RichBlockTableTest.php | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Type/RichBlockTable.php b/src/Type/RichBlockTable.php index 829ea5b3..371b7835 100644 --- a/src/Type/RichBlockTable.php +++ b/src/Type/RichBlockTable.php @@ -24,6 +24,7 @@ public function __construct( public ?true $isStriped = null, #[RichTextValue] public string|array|RichText|null $caption = null, + public ?true $isCompact = null, ) {} public function getType(): string diff --git a/tests/Type/RichBlockTableTest.php b/tests/Type/RichBlockTableTest.php index 5095041a..d3cb93a4 100644 --- a/tests/Type/RichBlockTableTest.php +++ b/tests/Type/RichBlockTableTest.php @@ -31,12 +31,13 @@ public function testBase(): void assertNull($table->isBordered); assertNull($table->isStriped); assertNull($table->caption); + assertNull($table->isCompact); } public function testFull(): void { $cell = new RichBlockTableCell('center', 'middle'); - $table = new RichBlockTable([[$cell]], true, true, 'caption'); + $table = new RichBlockTable([[$cell]], true, true, 'caption', true); assertSame('table', $table->getType()); assertCount(1, $table->cells); @@ -46,6 +47,7 @@ public function testFull(): void assertTrue($table->isBordered); assertTrue($table->isStriped); assertSame('caption', $table->caption); + assertTrue($table->isCompact); } public function testFromTelegramResult(): void @@ -68,6 +70,7 @@ public function testFromTelegramResult(): void assertNull($table->isBordered); assertNull($table->isStriped); assertNull($table->caption); + assertNull($table->isCompact); } public function testFromTelegramResultFull(): void @@ -81,6 +84,7 @@ public function testFromTelegramResultFull(): void ], 'is_bordered' => true, 'is_striped' => true, + 'is_compact' => true, 'caption' => ['type' => 'bold', 'text' => 'caption'], ], null, RichBlockTable::class); @@ -94,5 +98,6 @@ public function testFromTelegramResultFull(): void assertTrue($table->isStriped); assertInstanceOf(RichTextBold::class, $table->caption); assertSame('caption', $table->caption->text); + assertTrue($table->isCompact); } } From eded3ae3ae45fff24453582cb0cee508addb6107 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:54:51 +0300 Subject: [PATCH 11/37] Add isCompact field to InputRichBlockTable type --- src/Type/InputRichBlockTable.php | 2 ++ tests/Type/InputRichBlockTableTest.php | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Type/InputRichBlockTable.php b/src/Type/InputRichBlockTable.php index 8ffeb894..9b49ede5 100644 --- a/src/Type/InputRichBlockTable.php +++ b/src/Type/InputRichBlockTable.php @@ -22,6 +22,7 @@ public function __construct( public ?true $isBordered = null, public ?true $isStriped = null, public string|array|RichText|null $caption = null, + public ?true $isCompact = null, ) {} public function getType(): string @@ -46,6 +47,7 @@ public function toRequestArray(?FileCollector $fileCollector = null): array ), 'is_bordered' => $this->isBordered, 'is_striped' => $this->isStriped, + 'is_compact' => $this->isCompact, 'caption' => RichTextConverter::toRequestArray($this->caption), ], static fn(mixed $value): bool => $value !== null, diff --git a/tests/Type/InputRichBlockTableTest.php b/tests/Type/InputRichBlockTableTest.php index 9ed12711..4cf7fa18 100644 --- a/tests/Type/InputRichBlockTableTest.php +++ b/tests/Type/InputRichBlockTableTest.php @@ -27,7 +27,7 @@ public function testBase(): void public function testFull(): void { $cell = new RichBlockTableCell('left', 'top', 'hello'); - $table = new InputRichBlockTable([[$cell]], true, true, 'caption'); + $table = new InputRichBlockTable([[$cell]], true, true, 'caption', true); assertSame( [ @@ -35,6 +35,7 @@ public function testFull(): void 'cells' => [[['align' => 'left', 'valign' => 'top', 'text' => 'hello']]], 'is_bordered' => true, 'is_striped' => true, + 'is_compact' => true, 'caption' => 'caption', ], $table->toRequestArray(), From 5d212e48d37e97c64426642d62edd757f049e471 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:55:14 +0300 Subject: [PATCH 12/37] Add RichBlockExpandableBlockQuotation type --- .../ValueProcessor/RichBlockValue.php | 2 + .../RichBlockExpandableBlockQuotation.php | 27 ++++++++ .../RichBlockExpandableBlockQuotationTest.php | 62 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 src/Type/RichBlockExpandableBlockQuotation.php create mode 100644 tests/Type/RichBlockExpandableBlockQuotationTest.php diff --git a/src/ParseResult/ValueProcessor/RichBlockValue.php b/src/ParseResult/ValueProcessor/RichBlockValue.php index b5eacbfa..b36db2bc 100644 --- a/src/ParseResult/ValueProcessor/RichBlockValue.php +++ b/src/ParseResult/ValueProcessor/RichBlockValue.php @@ -13,6 +13,7 @@ use Phptg\BotApi\Type\RichBlockCollage; use Phptg\BotApi\Type\RichBlockSlideshow; use Phptg\BotApi\Type\RichBlockDetails; +use Phptg\BotApi\Type\RichBlockExpandableBlockQuotation; use Phptg\BotApi\Type\RichBlockAnimation; use Phptg\BotApi\Type\RichBlockAudio; use Phptg\BotApi\Type\RichBlockPhoto; @@ -63,6 +64,7 @@ public function getClassMap(): array 'voice_note' => RichBlockVoiceNote::class, 'thinking' => RichBlockThinking::class, 'buttons' => RichBlockButtons::class, + 'expandable_blockquote' => RichBlockExpandableBlockQuotation::class, ]; } diff --git a/src/Type/RichBlockExpandableBlockQuotation.php b/src/Type/RichBlockExpandableBlockQuotation.php new file mode 100644 index 00000000..5ac2afb0 --- /dev/null +++ b/src/Type/RichBlockExpandableBlockQuotation.php @@ -0,0 +1,27 @@ +getType()); + assertSame('hello', $blockquote->text); + assertNull($blockquote->credit); + } + + public function testFull(): void + { + $blockquote = new RichBlockExpandableBlockQuotation('hello', 'credit'); + + assertSame('expandable_blockquote', $blockquote->getType()); + assertSame('hello', $blockquote->text); + assertSame('credit', $blockquote->credit); + } + + public function testFromTelegramResult(): void + { + $blockquote = (new ObjectFactory())->create([ + 'type' => 'expandable_blockquote', + 'text' => 'hello', + ], null, RichBlockExpandableBlockQuotation::class); + + assertSame('expandable_blockquote', $blockquote->getType()); + assertSame('hello', $blockquote->text); + assertNull($blockquote->credit); + } + + public function testFromTelegramResultFull(): void + { + $blockquote = (new ObjectFactory())->create([ + 'type' => 'expandable_blockquote', + 'text' => ['type' => 'bold', 'text' => 'hello'], + 'credit' => ['type' => 'bold', 'text' => 'author'], + ], null, RichBlockExpandableBlockQuotation::class); + + assertSame('expandable_blockquote', $blockquote->getType()); + assertInstanceOf(RichTextBold::class, $blockquote->text); + assertSame('hello', $blockquote->text->text); + assertInstanceOf(RichTextBold::class, $blockquote->credit); + assertSame('author', $blockquote->credit->text); + } +} From 2648917ba6aae307121e82d50dad25d094d6a902 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:55:15 +0300 Subject: [PATCH 13/37] Add InputRichBlockExpandableBlockQuotation type --- ...InputRichBlockExpandableBlockQuotation.php | 41 +++++++++++++++++++ ...tRichBlockExpandableBlockQuotationTest.php | 34 +++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/Type/InputRichBlockExpandableBlockQuotation.php create mode 100644 tests/Type/InputRichBlockExpandableBlockQuotationTest.php diff --git a/src/Type/InputRichBlockExpandableBlockQuotation.php b/src/Type/InputRichBlockExpandableBlockQuotation.php new file mode 100644 index 00000000..9d9a3e80 --- /dev/null +++ b/src/Type/InputRichBlockExpandableBlockQuotation.php @@ -0,0 +1,41 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + return array_filter( + [ + 'type' => $this->getType(), + 'text' => RichTextConverter::toRequestArray($this->text), + 'credit' => RichTextConverter::toRequestArray($this->credit), + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/tests/Type/InputRichBlockExpandableBlockQuotationTest.php b/tests/Type/InputRichBlockExpandableBlockQuotationTest.php new file mode 100644 index 00000000..3aee7ac4 --- /dev/null +++ b/tests/Type/InputRichBlockExpandableBlockQuotationTest.php @@ -0,0 +1,34 @@ +getType()); + assertSame( + ['type' => 'expandable_blockquote', 'text' => 'hello'], + $blockQuotation->toRequestArray(), + ); + } + + public function testFull(): void + { + $blockQuotation = new InputRichBlockExpandableBlockQuotation('hello', 'credit'); + + assertSame( + ['type' => 'expandable_blockquote', 'text' => 'hello', 'credit' => 'credit'], + $blockQuotation->toRequestArray(), + ); + } +} From ff8b189bed2d4785601a0fb7f6e2148da05b2ad8 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:55:54 +0300 Subject: [PATCH 14/37] Add RichBlockDocument type --- .../ValueProcessor/RichBlockValue.php | 2 + src/Type/RichBlockDocument.php | 23 ++++++ tests/Type/RichBlockDocumentTest.php | 72 +++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 src/Type/RichBlockDocument.php create mode 100644 tests/Type/RichBlockDocumentTest.php diff --git a/src/ParseResult/ValueProcessor/RichBlockValue.php b/src/ParseResult/ValueProcessor/RichBlockValue.php index b36db2bc..3148ab71 100644 --- a/src/ParseResult/ValueProcessor/RichBlockValue.php +++ b/src/ParseResult/ValueProcessor/RichBlockValue.php @@ -13,6 +13,7 @@ use Phptg\BotApi\Type\RichBlockCollage; use Phptg\BotApi\Type\RichBlockSlideshow; use Phptg\BotApi\Type\RichBlockDetails; +use Phptg\BotApi\Type\RichBlockDocument; use Phptg\BotApi\Type\RichBlockExpandableBlockQuotation; use Phptg\BotApi\Type\RichBlockAnimation; use Phptg\BotApi\Type\RichBlockAudio; @@ -65,6 +66,7 @@ public function getClassMap(): array 'thinking' => RichBlockThinking::class, 'buttons' => RichBlockButtons::class, 'expandable_blockquote' => RichBlockExpandableBlockQuotation::class, + 'document' => RichBlockDocument::class, ]; } diff --git a/src/Type/RichBlockDocument.php b/src/Type/RichBlockDocument.php new file mode 100644 index 00000000..f81b25cc --- /dev/null +++ b/src/Type/RichBlockDocument.php @@ -0,0 +1,23 @@ +getType()); + assertSame($document, $block->document); + assertNull($block->caption); + } + + public function testFull(): void + { + $document = new Document('f123', 'fullF123'); + $caption = new RichBlockCaption('document'); + $block = new RichBlockDocument($document, $caption); + + assertSame('document', $block->getType()); + assertSame($document, $block->document); + assertSame($caption, $block->caption); + } + + public function testFromTelegramResult(): void + { + $block = (new ObjectFactory())->create([ + 'type' => 'document', + 'document' => [ + 'file_id' => 'f123', + 'file_unique_id' => 'fullF123', + ], + ], null, RichBlockDocument::class); + + assertSame('document', $block->getType()); + assertInstanceOf(Document::class, $block->document); + assertSame('f123', $block->document->fileId); + assertNull($block->caption); + } + + public function testFromTelegramResultFull(): void + { + $block = (new ObjectFactory())->create([ + 'type' => 'document', + 'document' => [ + 'file_id' => 'f123', + 'file_unique_id' => 'fullF123', + ], + 'caption' => ['text' => 'document'], + ], null, RichBlockDocument::class); + + assertSame('document', $block->getType()); + assertInstanceOf(Document::class, $block->document); + assertInstanceOf(RichBlockCaption::class, $block->caption); + assertSame('document', $block->caption->text); + } +} From 9545577ea32fec93d4a532b57bac21a5e95b28bb Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:55:55 +0300 Subject: [PATCH 15/37] Add InputRichBlockDocument type --- src/Type/InputRichBlockDocument.php | 40 +++++++++++++++ tests/Type/InputRichBlockDocumentTest.php | 61 +++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/Type/InputRichBlockDocument.php create mode 100644 tests/Type/InputRichBlockDocumentTest.php diff --git a/src/Type/InputRichBlockDocument.php b/src/Type/InputRichBlockDocument.php new file mode 100644 index 00000000..413ca58a --- /dev/null +++ b/src/Type/InputRichBlockDocument.php @@ -0,0 +1,40 @@ + + */ + public function toRequestArray(?FileCollector $fileCollector = null): array + { + return array_filter( + [ + 'type' => $this->getType(), + 'document' => $this->document->toRequestArray($fileCollector), + 'caption' => $this->caption?->toRequestArray(), + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/tests/Type/InputRichBlockDocumentTest.php b/tests/Type/InputRichBlockDocumentTest.php new file mode 100644 index 00000000..b3aa6bb8 --- /dev/null +++ b/tests/Type/InputRichBlockDocumentTest.php @@ -0,0 +1,61 @@ +getType()); + assertSame( + [ + 'type' => 'document', + 'document' => ['type' => 'document', 'media' => 'document_file_id_1'], + ], + $document->toRequestArray(), + ); + } + + public function testFull(): void + { + $document = new InputRichBlockDocument( + new InputMediaDocument('document_file_id_1'), + new RichBlockCaption('caption'), + ); + + assertSame( + [ + 'type' => 'document', + 'document' => ['type' => 'document', 'media' => 'document_file_id_1'], + 'caption' => ['text' => 'caption'], + ], + $document->toRequestArray(), + ); + } + + public function testFileCollectorIsPropagated(): void + { + $file = new InputFile(null); + $document = new InputRichBlockDocument(new InputMediaDocument($file)); + + $fileCollector = new FileCollector(); + assertSame( + ['type' => 'document', 'document' => ['type' => 'document', 'media' => 'attach://file0']], + $document->toRequestArray($fileCollector), + ); + assertSame(['file0' => $file], $fileCollector->get()); + } +} From 38799e999d16abea99460f7ce06fab99eb65c8ed Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:56:08 +0300 Subject: [PATCH 16/37] Add EphemeralMessageParameters type --- src/Type/EphemeralMessageParameters.php | 34 +++++++++++++ tests/Type/EphemeralMessageParametersTest.php | 49 +++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/Type/EphemeralMessageParameters.php create mode 100644 tests/Type/EphemeralMessageParametersTest.php diff --git a/src/Type/EphemeralMessageParameters.php b/src/Type/EphemeralMessageParameters.php new file mode 100644 index 00000000..602646df --- /dev/null +++ b/src/Type/EphemeralMessageParameters.php @@ -0,0 +1,34 @@ + + */ + public function toRequestArray(): array + { + return array_filter( + [ + 'receiver_user_id' => $this->receiverUserId, + 'callback_query_id' => $this->callbackQueryId, + 'replace_callback_query_message' => $this->replaceCallbackQueryMessage, + ], + static fn(mixed $value): bool => $value !== null, + ); + } +} diff --git a/tests/Type/EphemeralMessageParametersTest.php b/tests/Type/EphemeralMessageParametersTest.php new file mode 100644 index 00000000..a2eabff6 --- /dev/null +++ b/tests/Type/EphemeralMessageParametersTest.php @@ -0,0 +1,49 @@ +receiverUserId); + assertNull($parameters->callbackQueryId); + assertNull($parameters->replaceCallbackQueryMessage); + + assertSame( + [ + 'receiver_user_id' => 1, + ], + $parameters->toRequestArray(), + ); + } + + public function testFull(): void + { + $parameters = new EphemeralMessageParameters(1, 'query-id', true); + + assertSame(1, $parameters->receiverUserId); + assertSame('query-id', $parameters->callbackQueryId); + assertTrue($parameters->replaceCallbackQueryMessage); + + assertSame( + [ + 'receiver_user_id' => 1, + 'callback_query_id' => 'query-id', + 'replace_callback_query_message' => true, + ], + $parameters->toRequestArray(), + ); + } +} From c231cb994ed5d680b5295dd3c4b0d4340fac482e Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:57:47 +0300 Subject: [PATCH 17/37] Replace receiverUserId and callbackQueryId parameters with ephemeralMessageParameters in send methods --- src/Method/SendAnimation.php | 7 +-- src/Method/SendAudio.php | 7 +-- src/Method/SendContact.php | 7 +-- src/Method/SendDocument.php | 7 +-- src/Method/SendLivePhoto.php | 3 + src/Method/SendLocation.php | 7 +-- src/Method/SendMessage.php | 7 +-- src/Method/SendPhoto.php | 7 +-- src/Method/SendVenue.php | 7 +-- src/Method/SendVideo.php | 7 +-- src/Method/SendVideoNote.php | 7 +-- src/Method/SendVoice.php | 7 +-- src/Method/Sticker/SendSticker.php | 7 +-- src/TelegramBotApi.php | 75 +++++++++--------------- tests/Method/SendAnimationTest.php | 11 ++-- tests/Method/SendAudioTest.php | 11 ++-- tests/Method/SendContactTest.php | 11 ++-- tests/Method/SendDocumentTest.php | 11 ++-- tests/Method/SendLivePhotoTest.php | 7 +++ tests/Method/SendLocationTest.php | 11 ++-- tests/Method/SendMessageTest.php | 11 ++-- tests/Method/SendPhotoTest.php | 11 ++-- tests/Method/SendVenueTest.php | 11 ++-- tests/Method/SendVideoNoteTest.php | 11 ++-- tests/Method/SendVideoTest.php | 11 ++-- tests/Method/SendVoiceTest.php | 11 ++-- tests/Method/Sticker/SendStickerTest.php | 11 ++-- 27 files changed, 157 insertions(+), 144 deletions(-) diff --git a/src/Method/SendAnimation.php b/src/Method/SendAnimation.php index 160de3d5..94cdf53a 100644 --- a/src/Method/SendAnimation.php +++ b/src/Method/SendAnimation.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -49,8 +50,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -71,8 +71,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'animation' => $this->animation, 'duration' => $this->duration, 'width' => $this->width, diff --git a/src/Method/SendAudio.php b/src/Method/SendAudio.php index cd2e5371..4a844447 100644 --- a/src/Method/SendAudio.php +++ b/src/Method/SendAudio.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -47,8 +48,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -69,8 +69,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'audio' => $this->audio, 'caption' => $this->caption, 'parse_mode' => $this->parseMode, diff --git a/src/Method/SendContact.php b/src/Method/SendContact.php index 675653f0..476da337 100644 --- a/src/Method/SendContact.php +++ b/src/Method/SendContact.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\Message; @@ -38,8 +39,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -60,8 +60,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'phone_number' => $this->phoneNumber, 'first_name' => $this->firstName, 'last_name' => $this->lastName, diff --git a/src/Method/SendDocument.php b/src/Method/SendDocument.php index 4d9dd601..1bfdbaf4 100644 --- a/src/Method/SendDocument.php +++ b/src/Method/SendDocument.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -45,8 +46,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -67,8 +67,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'document' => $this->document, 'thumbnail' => $this->thumbnail, 'caption' => $this->caption, diff --git a/src/Method/SendLivePhoto.php b/src/Method/SendLivePhoto.php index 34327af5..b5427113 100644 --- a/src/Method/SendLivePhoto.php +++ b/src/Method/SendLivePhoto.php @@ -8,6 +8,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\InlineKeyboardMarkup; @@ -47,6 +48,7 @@ public function __construct( private ?SuggestedPostParameters $suggestedPostParameters = null, private ?ReplyParameters $replyParameters = null, private InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -75,6 +77,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'live_photo' => $livePhoto, 'photo' => $photo, 'caption' => $this->caption, diff --git a/src/Method/SendLocation.php b/src/Method/SendLocation.php index c7c65d25..490a0b20 100644 --- a/src/Method/SendLocation.php +++ b/src/Method/SendLocation.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\Message; @@ -40,8 +41,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -62,8 +62,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'latitude' => $this->latitude, 'longitude' => $this->longitude, 'horizontal_accuracy' => $this->horizontalAccuracy, diff --git a/src/Method/SendMessage.php b/src/Method/SendMessage.php index 08dd06a8..cdacb728 100644 --- a/src/Method/SendMessage.php +++ b/src/Method/SendMessage.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\LinkPreviewOptions; @@ -43,8 +44,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -65,8 +65,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'text' => $this->text, 'parse_mode' => $this->parseMode, 'entities' => $this->entities === null ? null : array_map( diff --git a/src/Method/SendPhoto.php b/src/Method/SendPhoto.php index 121d7aba..7acd9e2a 100644 --- a/src/Method/SendPhoto.php +++ b/src/Method/SendPhoto.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -45,8 +46,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -68,8 +68,7 @@ public function getData(): array 'business_connection_id' => $this->businessConnectionId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'caption' => $this->caption, 'parse_mode' => $this->parseMode, 'caption_entities' => $this->captionEntities === null ? null : array_map( diff --git a/src/Method/SendVenue.php b/src/Method/SendVenue.php index 2b71c40e..7286bcc1 100644 --- a/src/Method/SendVenue.php +++ b/src/Method/SendVenue.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\Message; @@ -42,8 +43,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -64,8 +64,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'latitude' => $this->latitude, 'longitude' => $this->longitude, 'title' => $this->title, diff --git a/src/Method/SendVideo.php b/src/Method/SendVideo.php index b45ba97a..25d5d5ae 100644 --- a/src/Method/SendVideo.php +++ b/src/Method/SendVideo.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -52,8 +53,7 @@ public function __construct( private ?int $startTimestamp = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -74,8 +74,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'video' => $this->video, 'duration' => $this->duration, 'width' => $this->width, diff --git a/src/Method/SendVideoNote.php b/src/Method/SendVideoNote.php index 3a7b69f8..7da146de 100644 --- a/src/Method/SendVideoNote.php +++ b/src/Method/SendVideoNote.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -39,8 +40,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -61,8 +61,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'video_note' => $this->videoNote, 'duration' => $this->duration, 'length' => $this->length, diff --git a/src/Method/SendVoice.php b/src/Method/SendVoice.php index a99cd847..163c4f1a 100644 --- a/src/Method/SendVoice.php +++ b/src/Method/SendVoice.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -44,8 +45,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -66,8 +66,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'voice' => $this->voice, 'caption' => $this->caption, 'parse_mode' => $this->parseMode, diff --git a/src/Method/Sticker/SendSticker.php b/src/Method/Sticker/SendSticker.php index 3d3cf597..c525605f 100644 --- a/src/Method/Sticker/SendSticker.php +++ b/src/Method/Sticker/SendSticker.php @@ -7,6 +7,7 @@ use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputFile; @@ -37,8 +38,7 @@ public function __construct( private ?bool $allowPaidBroadcast = null, private ?int $directMessagesTopicId = null, private ?SuggestedPostParameters $suggestedPostParameters = null, - private ?int $receiverUserId = null, - private ?string $callbackQueryId = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -59,8 +59,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, - 'receiver_user_id' => $this->receiverUserId, - 'callback_query_id' => $this->callbackQueryId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'sticker' => $this->sticker, 'emoji' => $this->emoji, 'disable_notification' => $this->disableNotification, diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 7eac0419..90028373 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -211,6 +211,7 @@ use Phptg\BotApi\Type\ChatInviteLink; use Phptg\BotApi\Type\ChatMember; use Phptg\BotApi\Type\ChatPermissions; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\File; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\ForumTopic; @@ -2070,8 +2071,7 @@ public function sendAnimation( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendAnimation( @@ -2096,8 +2096,7 @@ public function sendAnimation( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2127,8 +2126,7 @@ public function sendAudio( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendAudio( @@ -2151,8 +2149,7 @@ public function sendAudio( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2195,8 +2192,7 @@ public function sendContact( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendContact( @@ -2215,8 +2211,7 @@ public function sendContact( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2306,8 +2301,7 @@ public function sendDocument( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendDocument( @@ -2328,8 +2322,7 @@ public function sendDocument( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2490,8 +2483,7 @@ public function sendLocation( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendLocation( @@ -2512,8 +2504,7 @@ public function sendLocation( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2576,6 +2567,7 @@ public function sendLivePhoto( ?SuggestedPostParameters $suggestedPostParameters = null, ?ReplyParameters $replyParameters = null, InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendLivePhoto( @@ -2597,6 +2589,7 @@ public function sendLivePhoto( $suggestedPostParameters, $replyParameters, $replyMarkup, + $ephemeralMessageParameters, ), ); } @@ -2622,8 +2615,7 @@ public function sendMessage( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendMessage( @@ -2642,8 +2634,7 @@ public function sendMessage( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2744,8 +2735,7 @@ public function sendPhoto( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendPhoto( @@ -2766,8 +2756,7 @@ public function sendPhoto( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2875,8 +2864,7 @@ public function sendSticker( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendSticker( @@ -2893,8 +2881,7 @@ public function sendSticker( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2922,8 +2909,7 @@ public function sendVenue( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendVenue( @@ -2946,8 +2932,7 @@ public function sendVenue( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -2982,8 +2967,7 @@ public function sendVideo( ?int $startTimestamp = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendVideo( @@ -3011,8 +2995,7 @@ public function sendVideo( $startTimestamp, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -3036,8 +3019,7 @@ public function sendVideoNote( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendVideoNote( @@ -3056,8 +3038,7 @@ public function sendVideoNote( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } @@ -3084,8 +3065,7 @@ public function sendVoice( ?bool $allowPaidBroadcast = null, ?int $directMessagesTopicId = null, ?SuggestedPostParameters $suggestedPostParameters = null, - ?int $receiverUserId = null, - ?string $callbackQueryId = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendVoice( @@ -3105,8 +3085,7 @@ public function sendVoice( $allowPaidBroadcast, $directMessagesTopicId, $suggestedPostParameters, - $receiverUserId, - $callbackQueryId, + $ephemeralMessageParameters, ), ); } diff --git a/tests/Method/SendAnimationTest.php b/tests/Method/SendAnimationTest.php index f2d0e9fb..866a9bc2 100644 --- a/tests/Method/SendAnimationTest.php +++ b/tests/Method/SendAnimationTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendAnimation; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -65,8 +66,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -75,8 +75,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'animation' => $animation, 'duration' => 100, 'width' => 240, diff --git a/tests/Method/SendAudioTest.php b/tests/Method/SendAudioTest.php index 64422e25..afbae5e8 100644 --- a/tests/Method/SendAudioTest.php +++ b/tests/Method/SendAudioTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendAudio; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -63,8 +64,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -73,8 +73,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'audio' => $audio, 'caption' => 'Caption', 'parse_mode' => 'HTML', diff --git a/tests/Method/SendContactTest.php b/tests/Method/SendContactTest.php index 65663855..c324632e 100644 --- a/tests/Method/SendContactTest.php +++ b/tests/Method/SendContactTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendContact; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\ReplyParameters; use Phptg\BotApi\Type\SuggestedPostParameters; @@ -55,8 +56,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -65,8 +65,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'phone_number' => '1234567890', 'first_name' => 'John', 'last_name' => 'Doe', diff --git a/tests/Method/SendDocumentTest.php b/tests/Method/SendDocumentTest.php index 98da3f5c..8b2fcf39 100644 --- a/tests/Method/SendDocumentTest.php +++ b/tests/Method/SendDocumentTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendDocument; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -61,8 +62,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -71,8 +71,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'document' => $document, 'thumbnail' => $thumbnail, 'caption' => 'Caption', diff --git a/tests/Method/SendLivePhotoTest.php b/tests/Method/SendLivePhotoTest.php index 46c31fd3..ce2ced31 100644 --- a/tests/Method/SendLivePhotoTest.php +++ b/tests/Method/SendLivePhotoTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendLivePhoto; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -67,6 +68,7 @@ public function testFull(): void ), $replyParameters, $replyMarkup, + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -75,6 +77,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'live_photo' => 'attach://file0', 'photo' => 'attach://file1', 'caption' => 'Caption', diff --git a/tests/Method/SendLocationTest.php b/tests/Method/SendLocationTest.php index e8d9a61a..ed564425 100644 --- a/tests/Method/SendLocationTest.php +++ b/tests/Method/SendLocationTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendLocation; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\ReplyParameters; use Phptg\BotApi\Type\SuggestedPostParameters; @@ -57,8 +58,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -67,8 +67,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'latitude' => 1.1, 'longitude' => 2.2, 'horizontal_accuracy' => 2.23, diff --git a/tests/Method/SendMessageTest.php b/tests/Method/SendMessageTest.php index ff6bb7a7..03d931f9 100644 --- a/tests/Method/SendMessageTest.php +++ b/tests/Method/SendMessageTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendMessage; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\LinkPreviewOptions; use Phptg\BotApi\Type\MessageEntity; @@ -58,8 +59,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -68,8 +68,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'text' => 'hello', 'parse_mode' => 'parse', 'entities' => [$entity->toRequestArray()], diff --git a/tests/Method/SendPhotoTest.php b/tests/Method/SendPhotoTest.php index c59f4a4f..61869de5 100644 --- a/tests/Method/SendPhotoTest.php +++ b/tests/Method/SendPhotoTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendPhoto; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -60,8 +61,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -71,8 +71,11 @@ public function testFull(): void 'business_connection_id' => 'bcid1', 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'caption' => 'Caption', 'parse_mode' => 'parse', 'caption_entities' => [$entity->toRequestArray()], diff --git a/tests/Method/SendVenueTest.php b/tests/Method/SendVenueTest.php index 7a3513ea..6e295eed 100644 --- a/tests/Method/SendVenueTest.php +++ b/tests/Method/SendVenueTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendVenue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\ReplyParameters; use Phptg\BotApi\Type\SuggestedPostParameters; @@ -61,8 +62,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -71,8 +71,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'latitude' => 1.1, 'longitude' => 2.2, 'title' => 'Moscow', diff --git a/tests/Method/SendVideoNoteTest.php b/tests/Method/SendVideoNoteTest.php index 3434eb1d..94b6e982 100644 --- a/tests/Method/SendVideoNoteTest.php +++ b/tests/Method/SendVideoNoteTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendVideoNote; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\ReplyParameters; @@ -57,8 +58,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -67,8 +67,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'video_note' => $video, 'duration' => 500, 'length' => 240, diff --git a/tests/Method/SendVideoTest.php b/tests/Method/SendVideoTest.php index 5e3d5f66..1a82f801 100644 --- a/tests/Method/SendVideoTest.php +++ b/tests/Method/SendVideoTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendVideo; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -68,8 +69,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -78,8 +78,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'video' => $video, 'duration' => 500, 'width' => 300, diff --git a/tests/Method/SendVoiceTest.php b/tests/Method/SendVoiceTest.php index d7327261..4e821f1f 100644 --- a/tests/Method/SendVoiceTest.php +++ b/tests/Method/SendVoiceTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendVoice; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\MessageEntity; @@ -59,8 +60,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -69,8 +69,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'voice' => $voice, 'caption' => 'Caption', 'parse_mode' => 'HTML', diff --git a/tests/Method/Sticker/SendStickerTest.php b/tests/Method/Sticker/SendStickerTest.php index a25571b4..b56f1f38 100644 --- a/tests/Method/Sticker/SendStickerTest.php +++ b/tests/Method/Sticker/SendStickerTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\Sticker\SendSticker; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\Tests\Support\TestHelper; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputFile; use Phptg\BotApi\Type\ReplyParameters; @@ -54,8 +55,7 @@ public function testFull(): void new SuggestedPostParameters( new SuggestedPostPrice('USD', 10), ), - 789, - 'cbq1', + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -64,8 +64,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, - 'receiver_user_id' => 789, - 'callback_query_id' => 'cbq1', + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'sticker' => $sticker, 'emoji' => '👍', 'disable_notification' => true, From 92d46c1952330242d0453aa70269a37346b93b1d Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:58:18 +0300 Subject: [PATCH 18/37] Add ephemeralMessageParameters parameter to SendRichMessage method --- src/Method/SendRichMessage.php | 3 +++ src/TelegramBotApi.php | 2 ++ tests/Method/SendRichMessageTest.php | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/src/Method/SendRichMessage.php b/src/Method/SendRichMessage.php index a30f2530..9bdd1d26 100644 --- a/src/Method/SendRichMessage.php +++ b/src/Method/SendRichMessage.php @@ -8,6 +8,7 @@ use Phptg\BotApi\MethodInterface; use Phptg\BotApi\ParseResult\ValueProcessor\ObjectValue; use Phptg\BotApi\Transport\HttpMethod; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InlineKeyboardMarkup; use Phptg\BotApi\Type\InputRichMessage; @@ -37,6 +38,7 @@ public function __construct( private ?SuggestedPostParameters $suggestedPostParameters = null, private ?ReplyParameters $replyParameters = null, private InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, + private ?EphemeralMessageParameters $ephemeralMessageParameters = null, ) {} public function getHttpMethod(): HttpMethod @@ -59,6 +61,7 @@ public function getData(): array 'chat_id' => $this->chatId, 'message_thread_id' => $this->messageThreadId, 'direct_messages_topic_id' => $this->directMessagesTopicId, + 'ephemeral_message_parameters' => $this->ephemeralMessageParameters?->toRequestArray(), 'rich_message' => $this->richMessage->toRequestArray($fileCollector), 'disable_notification' => $this->disableNotification, 'protect_content' => $this->protectContent, diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 90028373..03300054 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -3114,6 +3114,7 @@ public function sendRichMessage( ?SuggestedPostParameters $suggestedPostParameters = null, ?ReplyParameters $replyParameters = null, InlineKeyboardMarkup|ReplyKeyboardMarkup|ReplyKeyboardRemove|ForceReply|null $replyMarkup = null, + ?EphemeralMessageParameters $ephemeralMessageParameters = null, ): FailResult|Message { return $this->call( new SendRichMessage( @@ -3129,6 +3130,7 @@ public function sendRichMessage( $suggestedPostParameters, $replyParameters, $replyMarkup, + $ephemeralMessageParameters, ), ); } diff --git a/tests/Method/SendRichMessageTest.php b/tests/Method/SendRichMessageTest.php index 9c7b11a8..643b1bee 100644 --- a/tests/Method/SendRichMessageTest.php +++ b/tests/Method/SendRichMessageTest.php @@ -8,6 +8,7 @@ use Phptg\BotApi\Method\SendRichMessage; use Phptg\BotApi\Tests\Support\TestHelper; use Phptg\BotApi\Transport\HttpMethod; +use Phptg\BotApi\Type\EphemeralMessageParameters; use Phptg\BotApi\Type\ForceReply; use Phptg\BotApi\Type\InputRichMessage; use Phptg\BotApi\Type\ReplyParameters; @@ -52,6 +53,7 @@ public function testFull(): void $suggestedPostParameters, $replyParameters, $replyMarkup, + new EphemeralMessageParameters(789, 'cbq1', true), ); assertSame( @@ -60,6 +62,11 @@ public function testFull(): void 'chat_id' => 12, 'message_thread_id' => 99, 'direct_messages_topic_id' => 123, + 'ephemeral_message_parameters' => [ + 'receiver_user_id' => 789, + 'callback_query_id' => 'cbq1', + 'replace_callback_query_message' => true, + ], 'rich_message' => ['html' => 'Hello', 'is_rtl' => true], 'disable_notification' => true, 'protect_content' => false, From d43e23893b8b2cea6ed131a687868e838769f4e2 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:58:44 +0300 Subject: [PATCH 19/37] Add showCaptionAboveMedia parameter to EditEphemeralMessageCaption method --- src/Method/UpdatingMessage/EditEphemeralMessageCaption.php | 2 ++ src/TelegramBotApi.php | 2 ++ .../Method/UpdatingMessage/EditEphemeralMessageCaptionTest.php | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/Method/UpdatingMessage/EditEphemeralMessageCaption.php b/src/Method/UpdatingMessage/EditEphemeralMessageCaption.php index a73ca796..b273bd86 100644 --- a/src/Method/UpdatingMessage/EditEphemeralMessageCaption.php +++ b/src/Method/UpdatingMessage/EditEphemeralMessageCaption.php @@ -28,6 +28,7 @@ public function __construct( private ?string $parseMode = null, private ?array $captionEntities = null, private ?InlineKeyboardMarkup $replyMarkup = null, + private ?bool $showCaptionAboveMedia = null, ) {} public function getHttpMethod(): HttpMethod @@ -53,6 +54,7 @@ public function getData(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->captionEntities, ), + 'show_caption_above_media' => $this->showCaptionAboveMedia, 'reply_markup' => $this->replyMarkup?->toRequestArray(), ], static fn(mixed $value): bool => $value !== null, diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 03300054..76a7b0a8 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -1017,6 +1017,7 @@ public function editEphemeralMessageCaption( ?string $parseMode = null, ?array $captionEntities = null, ?InlineKeyboardMarkup $replyMarkup = null, + ?bool $showCaptionAboveMedia = null, ): FailResult|true { return $this->call( new EditEphemeralMessageCaption( @@ -1027,6 +1028,7 @@ public function editEphemeralMessageCaption( $parseMode, $captionEntities, $replyMarkup, + $showCaptionAboveMedia, ), ); } diff --git a/tests/Method/UpdatingMessage/EditEphemeralMessageCaptionTest.php b/tests/Method/UpdatingMessage/EditEphemeralMessageCaptionTest.php index 30b26a83..3b806bcf 100644 --- a/tests/Method/UpdatingMessage/EditEphemeralMessageCaptionTest.php +++ b/tests/Method/UpdatingMessage/EditEphemeralMessageCaptionTest.php @@ -46,6 +46,7 @@ public function testFull(): void ParseMode::MARKDOWN_V2, [$messageEntity], $replyMarkup, + true, ); assertSame( @@ -56,6 +57,7 @@ public function testFull(): void 'caption' => 'test', 'parse_mode' => 'MarkdownV2', 'caption_entities' => [$messageEntity->toRequestArray()], + 'show_caption_above_media' => true, 'reply_markup' => $replyMarkup->toRequestArray(), ], $method->getData(), From b80639ec55cf48f213d802525bbf13a931941c45 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 10:59:21 +0300 Subject: [PATCH 20/37] Add richMessage parameter to EditEphemeralMessageText method --- src/Method/UpdatingMessage/EditEphemeralMessageText.php | 9 ++++++++- src/TelegramBotApi.php | 4 +++- .../UpdatingMessage/EditEphemeralMessageTextTest.php | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Method/UpdatingMessage/EditEphemeralMessageText.php b/src/Method/UpdatingMessage/EditEphemeralMessageText.php index ad1f041e..4bbe6b17 100644 --- a/src/Method/UpdatingMessage/EditEphemeralMessageText.php +++ b/src/Method/UpdatingMessage/EditEphemeralMessageText.php @@ -4,10 +4,12 @@ namespace Phptg\BotApi\Method\UpdatingMessage; +use Phptg\BotApi\FileCollector; use Phptg\BotApi\ParseResult\ValueProcessor\TrueValue; use Phptg\BotApi\Transport\HttpMethod; use Phptg\BotApi\MethodInterface; use Phptg\BotApi\Type\InlineKeyboardMarkup; +use Phptg\BotApi\Type\InputRichMessage; use Phptg\BotApi\Type\LinkPreviewOptions; use Phptg\BotApi\Type\MessageEntity; @@ -25,11 +27,12 @@ public function __construct( private int|string $chatId, private int $receiverUserId, private int $ephemeralMessageId, - private string $text, + private ?string $text = null, private ?string $parseMode = null, private ?array $entities = null, private ?LinkPreviewOptions $linkPreviewOptions = null, private ?InlineKeyboardMarkup $replyMarkup = null, + private ?InputRichMessage $richMessage = null, ) {} public function getHttpMethod(): HttpMethod @@ -44,6 +47,8 @@ public function getApiMethod(): string public function getData(): array { + $fileCollector = new FileCollector(); + return array_filter( [ 'chat_id' => $this->chatId, @@ -55,8 +60,10 @@ public function getData(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->entities, ), + 'rich_message' => $this->richMessage?->toRequestArray($fileCollector), 'link_preview_options' => $this->linkPreviewOptions?->toRequestArray(), 'reply_markup' => $this->replyMarkup?->toRequestArray(), + ...$fileCollector->get(), ], static fn(mixed $value): bool => $value !== null, ); diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 76a7b0a8..2bb30811 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -963,11 +963,12 @@ public function editEphemeralMessageText( int|string $chatId, int $receiverUserId, int $ephemeralMessageId, - string $text, + ?string $text = null, ?string $parseMode = null, ?array $entities = null, ?LinkPreviewOptions $linkPreviewOptions = null, ?InlineKeyboardMarkup $replyMarkup = null, + ?InputRichMessage $richMessage = null, ): FailResult|true { return $this->call( new EditEphemeralMessageText( @@ -979,6 +980,7 @@ public function editEphemeralMessageText( $entities, $linkPreviewOptions, $replyMarkup, + $richMessage, ), ); } diff --git a/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php b/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php index a9baf080..7d1adf03 100644 --- a/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php +++ b/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php @@ -11,6 +11,7 @@ use Phptg\BotApi\Tests\Support\TestHelper; use Phptg\BotApi\Type\InlineKeyboardButton; use Phptg\BotApi\Type\InlineKeyboardMarkup; +use Phptg\BotApi\Type\InputRichMessage; use Phptg\BotApi\Type\LinkPreviewOptions; use Phptg\BotApi\Type\MessageEntity; @@ -50,6 +51,7 @@ public function testFull(): void [$messageEntity], $linkPreviewOptions, $replyMarkup, + new InputRichMessage(html: 'hello'), ); assertSame( @@ -60,6 +62,7 @@ public function testFull(): void 'text' => 'hello', 'parse_mode' => 'MarkdownV2', 'entities' => [$messageEntity->toRequestArray()], + 'rich_message' => ['html' => 'hello'], 'link_preview_options' => $linkPreviewOptions->toRequestArray(), 'reply_markup' => $replyMarkup->toRequestArray(), ], From b97a30651edb4995f80d4dd55a49d9371b7e4993 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:00:06 +0300 Subject: [PATCH 21/37] Add canSendWelcomeMessages field to ChatAdministratorRights type --- src/Type/ChatAdministratorRights.php | 2 ++ tests/Type/ChatAdministratorRightsTest.php | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/Type/ChatAdministratorRights.php b/src/Type/ChatAdministratorRights.php index cc46e96a..a9c99c84 100644 --- a/src/Type/ChatAdministratorRights.php +++ b/src/Type/ChatAdministratorRights.php @@ -29,6 +29,7 @@ public function __construct( public ?bool $canManageTopics = null, public ?bool $canManageDirectMessages = null, public ?bool $canManageTags = null, + public ?bool $canSendWelcomeMessages = null, ) {} public function toRequestArray(): array @@ -52,6 +53,7 @@ public function toRequestArray(): array 'can_manage_topics' => $this->canManageTopics, 'can_manage_direct_messages' => $this->canManageDirectMessages, 'can_manage_tags' => $this->canManageTags, + 'can_send_welcome_messages' => $this->canSendWelcomeMessages, ], static fn(mixed $value): bool => $value !== null, ); diff --git a/tests/Type/ChatAdministratorRightsTest.php b/tests/Type/ChatAdministratorRightsTest.php index 867cfca5..d72b52cc 100644 --- a/tests/Type/ChatAdministratorRightsTest.php +++ b/tests/Type/ChatAdministratorRightsTest.php @@ -36,6 +36,7 @@ public function testBase(): void assertNull($rights->canManageTopics); assertNull($rights->canManageDirectMessages); assertNull($rights->canManageTags); + assertNull($rights->canSendWelcomeMessages); assertSame( [ @@ -75,6 +76,7 @@ public function testFromTelegramResult(): void 'can_manage_topics' => true, 'can_manage_direct_messages' => false, 'can_manage_tags' => true, + 'can_send_welcome_messages' => true, ], null, ChatAdministratorRights::class); assertTrue($type->isAnonymous); @@ -94,6 +96,7 @@ public function testFromTelegramResult(): void assertTrue($type->canManageTopics); assertFalse($type->canManageDirectMessages); assertTrue($type->canManageTags); + assertTrue($type->canSendWelcomeMessages); assertSame( [ 'is_anonymous' => true, @@ -113,6 +116,7 @@ public function testFromTelegramResult(): void 'can_manage_topics' => true, 'can_manage_direct_messages' => false, 'can_manage_tags' => true, + 'can_send_welcome_messages' => true, ], $type->toRequestArray(), ); From 8d8f60323d258c7425439a149f3b83e28406b10a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:00:26 +0300 Subject: [PATCH 22/37] Add canSendWelcomeMessages field to ChatMemberAdministrator type --- src/Type/ChatMemberAdministrator.php | 1 + tests/Type/ChatMemberAdministratorTest.php | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Type/ChatMemberAdministrator.php b/src/Type/ChatMemberAdministrator.php index c7829374..ea75df0c 100644 --- a/src/Type/ChatMemberAdministrator.php +++ b/src/Type/ChatMemberAdministrator.php @@ -32,6 +32,7 @@ public function __construct( public ?string $customTitle = null, public ?bool $canManageDirectMessages = null, public ?bool $canManageTags = null, + public ?bool $canSendWelcomeMessages = null, ) {} public function getStatus(): string diff --git a/tests/Type/ChatMemberAdministratorTest.php b/tests/Type/ChatMemberAdministratorTest.php index 20b95b19..a963aeb6 100644 --- a/tests/Type/ChatMemberAdministratorTest.php +++ b/tests/Type/ChatMemberAdministratorTest.php @@ -57,6 +57,7 @@ public function testBase(): void assertNull($member->customTitle); assertNull($member->canManageDirectMessages); assertNull($member->canManageTags); + assertNull($member->canSendWelcomeMessages); } public function testFromTelegramResult(): void @@ -85,6 +86,7 @@ public function testFromTelegramResult(): void 'can_manage_topics' => false, 'can_manage_direct_messages' => true, 'can_manage_tags' => false, + 'can_send_welcome_messages' => true, 'custom_title' => 'Custom title', ], null, ChatMemberAdministrator::class); @@ -108,5 +110,6 @@ public function testFromTelegramResult(): void assertSame('Custom title', $member->customTitle); assertTrue($member->canManageDirectMessages); assertFalse($member->canManageTags); + assertTrue($member->canSendWelcomeMessages); } } From ffda6b632ec20675e1186e856c7f3aedafac1273 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:00:51 +0300 Subject: [PATCH 23/37] Add canSendWelcomeMessages parameter to PromoteChatMember method --- src/Method/PromoteChatMember.php | 2 ++ src/TelegramBotApi.php | 2 ++ tests/Method/PromoteChatMemberTest.php | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/Method/PromoteChatMember.php b/src/Method/PromoteChatMember.php index b96164ce..5bc8e99d 100644 --- a/src/Method/PromoteChatMember.php +++ b/src/Method/PromoteChatMember.php @@ -35,6 +35,7 @@ public function __construct( private ?bool $canManageTopics = null, private ?bool $canManageDirectMessages = null, private ?bool $canManageTags = null, + private ?bool $canSendWelcomeMessages = null, ) {} public function getHttpMethod(): HttpMethod @@ -70,6 +71,7 @@ public function getData(): array 'can_manage_topics' => $this->canManageTopics, 'can_manage_direct_messages' => $this->canManageDirectMessages, 'can_manage_tags' => $this->canManageTags, + 'can_send_welcome_messages' => $this->canSendWelcomeMessages, ], static fn(mixed $value): bool => $value !== null, ); diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 2bb30811..3a79baf7 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -1861,6 +1861,7 @@ public function promoteChatMember( ?bool $canManageTopics = null, ?bool $canManageDirectMessages = null, ?bool $canManageTags = null, + ?bool $canSendWelcomeMessages = null, ): FailResult|true { return $this->call( new PromoteChatMember( @@ -1883,6 +1884,7 @@ public function promoteChatMember( $canManageTopics, $canManageDirectMessages, $canManageTags, + $canSendWelcomeMessages, ), ); } diff --git a/tests/Method/PromoteChatMemberTest.php b/tests/Method/PromoteChatMemberTest.php index 569b461f..c4b956a8 100644 --- a/tests/Method/PromoteChatMemberTest.php +++ b/tests/Method/PromoteChatMemberTest.php @@ -51,6 +51,7 @@ public function testFull(): void true, false, true, + false, ); assertSame( @@ -74,6 +75,7 @@ public function testFull(): void 'can_manage_topics' => true, 'can_manage_direct_messages' => false, 'can_manage_tags' => true, + 'can_send_welcome_messages' => false, ], $method->getData(), ); From b0490746704e143d417bf1536692fd7d87340274 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:01:13 +0300 Subject: [PATCH 24/37] Add canStop and keepOnStop parameters to SendMessageDraft method --- src/Method/SendMessageDraft.php | 4 ++++ src/TelegramBotApi.php | 4 ++++ tests/Method/SendMessageDraftTest.php | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/Method/SendMessageDraft.php b/src/Method/SendMessageDraft.php index 872f1f64..39fa00f1 100644 --- a/src/Method/SendMessageDraft.php +++ b/src/Method/SendMessageDraft.php @@ -26,6 +26,8 @@ public function __construct( private ?int $messageThreadId = null, private ?string $parseMode = null, private ?array $entities = null, + private ?bool $canStop = null, + private ?bool $keepOnStop = null, ) {} public function getHttpMethod(): HttpMethod @@ -51,6 +53,8 @@ public function getData(): array static fn(MessageEntity $entity) => $entity->toRequestArray(), $this->entities, ), + 'can_stop' => $this->canStop, + 'keep_on_stop' => $this->keepOnStop, ], static fn(mixed $value): bool => $value !== null, ); diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index 3a79baf7..dd9f687e 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -2657,6 +2657,8 @@ public function sendMessageDraft( ?int $messageThreadId = null, ?string $parseMode = null, ?array $entities = null, + ?bool $canStop = null, + ?bool $keepOnStop = null, ): FailResult|true { return $this->call( new SendMessageDraft( @@ -2666,6 +2668,8 @@ public function sendMessageDraft( $messageThreadId, $parseMode, $entities, + $canStop, + $keepOnStop, ), ); } diff --git a/tests/Method/SendMessageDraftTest.php b/tests/Method/SendMessageDraftTest.php index 88c345ef..5e4ec3c6 100644 --- a/tests/Method/SendMessageDraftTest.php +++ b/tests/Method/SendMessageDraftTest.php @@ -40,6 +40,8 @@ public function testFull(): void 99, 'HTML', [$entity], + true, + false, ); assertSame( @@ -50,6 +52,8 @@ public function testFull(): void 'text' => 'hello', 'parse_mode' => 'HTML', 'entities' => [$entity->toRequestArray()], + 'can_stop' => true, + 'keep_on_stop' => false, ], $method->getData(), ); From d3b722d6bcfe63177eed2d07898efb0ec326e951 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:01:21 +0300 Subject: [PATCH 25/37] Add canStop and keepOnStop parameters to SendRichMessageDraft method --- src/Method/SendRichMessageDraft.php | 4 ++++ src/TelegramBotApi.php | 4 ++++ tests/Method/SendRichMessageDraftTest.php | 4 +++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Method/SendRichMessageDraft.php b/src/Method/SendRichMessageDraft.php index 590358ee..c2e08bb4 100644 --- a/src/Method/SendRichMessageDraft.php +++ b/src/Method/SendRichMessageDraft.php @@ -22,6 +22,8 @@ public function __construct( private int $draftId, private InputRichMessage $richMessage, private ?int $messageThreadId = null, + private ?bool $canStop = null, + private ?bool $keepOnStop = null, ) {} public function getHttpMethod(): HttpMethod @@ -44,6 +46,8 @@ public function getData(): array 'message_thread_id' => $this->messageThreadId, 'draft_id' => $this->draftId, 'rich_message' => $this->richMessage->toRequestArray($fileCollector), + 'can_stop' => $this->canStop, + 'keep_on_stop' => $this->keepOnStop, ...$fileCollector->get(), ], static fn(mixed $value): bool => $value !== null, diff --git a/src/TelegramBotApi.php b/src/TelegramBotApi.php index dd9f687e..28330271 100644 --- a/src/TelegramBotApi.php +++ b/src/TelegramBotApi.php @@ -3153,6 +3153,8 @@ public function sendRichMessageDraft( int $draftId, InputRichMessage $richMessage, ?int $messageThreadId = null, + ?bool $canStop = null, + ?bool $keepOnStop = null, ): FailResult|true { return $this->call( new SendRichMessageDraft( @@ -3160,6 +3162,8 @@ public function sendRichMessageDraft( $draftId, $richMessage, $messageThreadId, + $canStop, + $keepOnStop, ), ); } diff --git a/tests/Method/SendRichMessageDraftTest.php b/tests/Method/SendRichMessageDraftTest.php index 00eea397..9c9547a9 100644 --- a/tests/Method/SendRichMessageDraftTest.php +++ b/tests/Method/SendRichMessageDraftTest.php @@ -35,7 +35,7 @@ public function testBase(): void public function testFull(): void { $richMessage = new InputRichMessage(html: 'Hello', isRtl: true); - $method = new SendRichMessageDraft(12, 1, $richMessage, 99); + $method = new SendRichMessageDraft(12, 1, $richMessage, 99, true, false); assertSame( [ @@ -43,6 +43,8 @@ public function testFull(): void 'message_thread_id' => 99, 'draft_id' => 1, 'rich_message' => $richMessage->toRequestArray(), + 'can_stop' => true, + 'keep_on_stop' => false, ], $method->getData(), ); From 9aa3d5ce53511e3a2647d1cab06738a5a713c7b9 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:01:41 +0300 Subject: [PATCH 26/37] Add MessageGenerationStopped type --- src/Type/MessageGenerationStopped.php | 19 ++++++ tests/Type/MessageGenerationStoppedTest.php | 64 +++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 src/Type/MessageGenerationStopped.php create mode 100644 tests/Type/MessageGenerationStoppedTest.php diff --git a/src/Type/MessageGenerationStopped.php b/src/Type/MessageGenerationStopped.php new file mode 100644 index 00000000..9e5e3b0f --- /dev/null +++ b/src/Type/MessageGenerationStopped.php @@ -0,0 +1,19 @@ +chat); + assertSame(100, $update->draftId); + assertNull($update->messageThreadId); + } + + public function testFull(): void + { + $chat = new Chat(1, 'private'); + $update = new MessageGenerationStopped($chat, 100, 99); + + assertSame($chat, $update->chat); + assertSame(100, $update->draftId); + assertSame(99, $update->messageThreadId); + } + + public function testFromTelegramResult(): void + { + $update = (new ObjectFactory())->create([ + 'chat' => ['id' => 1, 'type' => 'private'], + 'draft_id' => 100, + ], null, MessageGenerationStopped::class); + + assertInstanceOf(Chat::class, $update->chat); + assertSame(1, $update->chat->id); + assertSame(100, $update->draftId); + assertNull($update->messageThreadId); + } + + public function testFromTelegramResultFull(): void + { + $update = (new ObjectFactory())->create([ + 'chat' => ['id' => 1, 'type' => 'private'], + 'message_thread_id' => 99, + 'draft_id' => 100, + ], null, MessageGenerationStopped::class); + + assertInstanceOf(Chat::class, $update->chat); + assertSame(1, $update->chat->id); + assertSame(100, $update->draftId); + assertSame(99, $update->messageThreadId); + } +} From 3e074baaff5a369ab23451ee923255ef13e118d0 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:02:06 +0300 Subject: [PATCH 27/37] Add stoppedMessageGeneration field to Update type --- src/Type/Update/Update.php | 2 ++ tests/Type/Update/UpdateTest.php | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/Type/Update/Update.php b/src/Type/Update/Update.php index 9380cc98..f99ab46d 100644 --- a/src/Type/Update/Update.php +++ b/src/Type/Update/Update.php @@ -21,6 +21,7 @@ use Phptg\BotApi\Type\Inline\InlineQuery; use Phptg\BotApi\Type\ManagedBotUpdated; use Phptg\BotApi\Type\Message; +use Phptg\BotApi\Type\MessageGenerationStopped; use Phptg\BotApi\Type\MessageReactionCountUpdated; use Phptg\BotApi\Type\MessageReactionUpdated; use Phptg\BotApi\Type\Payment\BotSubscriptionUpdated; @@ -68,6 +69,7 @@ public function __construct( public readonly ?PaidMediaPurchased $purchasedPaidMedia = null, public readonly ?ManagedBotUpdated $managedBot = null, public readonly ?BotSubscriptionUpdated $subscription = null, + public readonly ?MessageGenerationStopped $stoppedMessageGeneration = null, ) {} /** diff --git a/tests/Type/Update/UpdateTest.php b/tests/Type/Update/UpdateTest.php index 14da2b81..480c8d21 100644 --- a/tests/Type/Update/UpdateTest.php +++ b/tests/Type/Update/UpdateTest.php @@ -49,6 +49,7 @@ public function testBase(): void assertNull($update->purchasedPaidMedia); assertNull($update->managedBot); assertNull($update->subscription); + assertNull($update->stoppedMessageGeneration); assertNull($update->getRaw()); assertNull($update->getRaw(true)); } @@ -372,6 +373,14 @@ public function testFromTelegramResult(): void 'invoice_payload' => 'sub_payload', 'state' => 'canceled', ], + 'stopped_message_generation' => [ + 'chat' => [ + 'id' => 654, + 'type' => 'private', + ], + 'message_thread_id' => 12, + 'draft_id' => 100, + ], ]; $update = (new ObjectFactory())->create($data, null, Update::class); @@ -406,6 +415,9 @@ public function testFromTelegramResult(): void assertSame(321, $update->subscription?->user->id); assertSame('sub_payload', $update->subscription?->invoicePayload); assertSame('canceled', $update->subscription?->state); + assertSame(654, $update->stoppedMessageGeneration?->chat->id); + assertSame(12, $update->stoppedMessageGeneration?->messageThreadId); + assertSame(100, $update->stoppedMessageGeneration?->draftId); assertNull($update->getRaw()); assertNull($update->getRaw(true)); } From c60129b51c36adbe4ac608ab5435f4dcbfdc7a0b Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:02:12 +0300 Subject: [PATCH 28/37] Add CommunityChatJoined type --- src/Type/CommunityChatJoined.php | 17 ++++++++++++ tests/Type/CommunityChatJoinedTest.php | 36 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/Type/CommunityChatJoined.php create mode 100644 tests/Type/CommunityChatJoinedTest.php diff --git a/src/Type/CommunityChatJoined.php b/src/Type/CommunityChatJoined.php new file mode 100644 index 00000000..e7e73a0a --- /dev/null +++ b/src/Type/CommunityChatJoined.php @@ -0,0 +1,17 @@ +community); + } + + public function testFromTelegramResult(): void + { + $communityChatJoined = (new ObjectFactory())->create([ + 'community' => [ + 'id' => 123, + 'name' => 'My Community', + ], + ], null, CommunityChatJoined::class); + + assertSame(123, $communityChatJoined->community->id); + assertSame('My Community', $communityChatJoined->community->name); + } +} From d41370a90ef139a365b3fd83c6d333debf2416ff Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:02:31 +0300 Subject: [PATCH 29/37] Add communityChatJoined field to Message type --- src/Type/Message.php | 1 + tests/Type/MessageTest.php | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/Type/Message.php b/src/Type/Message.php index 55236f83..c26bb80e 100644 --- a/src/Type/Message.php +++ b/src/Type/Message.php @@ -154,5 +154,6 @@ public function __construct( public ?int $ephemeralMessageId = null, public ?CommunityChatAdded $communityChatAdded = null, public ?CommunityChatRemoved $communityChatRemoved = null, + public ?CommunityChatJoined $communityChatJoined = null, ) {} } diff --git a/tests/Type/MessageTest.php b/tests/Type/MessageTest.php index 370cf693..25b39035 100644 --- a/tests/Type/MessageTest.php +++ b/tests/Type/MessageTest.php @@ -160,6 +160,7 @@ public function testBase(): void assertNull($message->ephemeralMessageId); assertNull($message->communityChatAdded); assertNull($message->communityChatRemoved); + assertNull($message->communityChatJoined); } public function testFromTelegramResult(): void @@ -718,6 +719,12 @@ public function testFromTelegramResult(): void ], ], 'community_chat_removed' => [], + 'community_chat_joined' => [ + 'community' => [ + 'id' => 654, + 'name' => 'Joined Community', + ], + ], ], null, Message::class); assertSame(7, $message->messageId); @@ -879,5 +886,7 @@ public function testFromTelegramResult(): void assertSame(987, $message->communityChatAdded?->community->id); assertSame('My Community', $message->communityChatAdded?->community->name); assertInstanceOf(CommunityChatRemoved::class, $message->communityChatRemoved); + assertSame(654, $message->communityChatJoined?->community->id); + assertSame('Joined Community', $message->communityChatJoined?->community->name); } } From 6e3131c6aa0dd857bb86c78c89ff3cfb883d0222 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:02:58 +0300 Subject: [PATCH 30/37] Add text, entities and isPrivate fields to UniqueGiftInfo type --- src/Type/UniqueGiftInfo.php | 8 ++++++++ tests/Type/UniqueGiftInfoTest.php | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/Type/UniqueGiftInfo.php b/src/Type/UniqueGiftInfo.php index 7772ddd9..e38d17f3 100644 --- a/src/Type/UniqueGiftInfo.php +++ b/src/Type/UniqueGiftInfo.php @@ -5,6 +5,7 @@ namespace Phptg\BotApi\Type; use DateTimeImmutable; +use Phptg\BotApi\ParseResult\ValueProcessor\ArrayOfObjectsValue; /** * @see https://core.telegram.org/bots/api#uniquegiftinfo @@ -13,6 +14,9 @@ */ final readonly class UniqueGiftInfo { + /** + * @param MessageEntity[]|null $entities + */ public function __construct( public UniqueGift $gift, public string $origin, @@ -21,5 +25,9 @@ public function __construct( public ?string $lastResaleCurrency = null, public ?int $lastResaleAmount = null, public ?DateTimeImmutable $nextTransferDate = null, + public ?string $text = null, + #[ArrayOfObjectsValue(MessageEntity::class)] + public ?array $entities = null, + public ?true $isPrivate = null, ) {} } diff --git a/tests/Type/UniqueGiftInfoTest.php b/tests/Type/UniqueGiftInfoTest.php index 0c896991..725b4cb3 100644 --- a/tests/Type/UniqueGiftInfoTest.php +++ b/tests/Type/UniqueGiftInfoTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; use Phptg\BotApi\ParseResult\ObjectFactory; +use Phptg\BotApi\Type\MessageEntity; use Phptg\BotApi\Type\Sticker\Sticker; use Phptg\BotApi\Type\UniqueGiftBackdrop; use Phptg\BotApi\Type\UniqueGiftBackdropColors; @@ -14,9 +15,12 @@ use Phptg\BotApi\Type\UniqueGiftModel; use Phptg\BotApi\Type\UniqueGiftSymbol; +use function PHPUnit\Framework\assertContainsOnlyInstancesOf; +use function PHPUnit\Framework\assertCount; use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertNull; use function PHPUnit\Framework\assertSame; +use function PHPUnit\Framework\assertTrue; final class UniqueGiftInfoTest extends TestCase { @@ -68,6 +72,9 @@ public function testBase(): void assertNull($uniqueGiftInfo->lastResaleCurrency); assertNull($uniqueGiftInfo->lastResaleAmount); assertNull($uniqueGiftInfo->nextTransferDate); + assertNull($uniqueGiftInfo->text); + assertNull($uniqueGiftInfo->entities); + assertNull($uniqueGiftInfo->isPrivate); } public function testFromTelegramResult(): void @@ -121,6 +128,11 @@ public function testFromTelegramResult(): void 'owned_gift_id' => 'owned-id1', 'transfer_star_count' => 15, 'next_transfer_date' => 1700000000, + 'text' => 'Happy birthday!', + 'entities' => [ + ['type' => 'bold', 'offset' => 0, 'length' => 5], + ], + 'is_private' => true, ], null, UniqueGiftInfo::class); assertInstanceOf(UniqueGiftInfo::class, $type); @@ -131,5 +143,10 @@ public function testFromTelegramResult(): void assertSame('XTR', $type->lastResaleCurrency); assertSame(99, $type->lastResaleAmount); assertSame(1700000000, $type->nextTransferDate?->getTimestamp()); + assertSame('Happy birthday!', $type->text); + assertContainsOnlyInstancesOf(MessageEntity::class, $type->entities); + assertCount(1, $type->entities); + assertSame('bold', $type->entities[0]->type); + assertTrue($type->isPrivate); } } From 82bf09aaf45ba6ab9d698badb7c02f01c4da1e19 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:03:06 +0300 Subject: [PATCH 31/37] Update supported Telegram Bot API version to 10.3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 981d35d8..1154bc51 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The package provides a simple and convenient way to interact with the Telegram Bot API. -✔️ Telegram Bot API 10.2 (July 14, 2026) is **fully supported**. +✔️ Telegram Bot API 10.3 (August 24, 2026) is **fully supported**. ♻️ **Zero dependencies** — no third-party libraries, only native PHP. From 2c987a836e0a2dae988129b27308121560651d9a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:03:28 +0300 Subject: [PATCH 32/37] Add CHANGELOG entries for Bot API 10.3 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 97bde805..0d3c0b52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ ## 0.21.3 under development +- New #217: Add `DisabledButton`, `RichMessageButton`, `RichTextButton`, `RichBlockButtons`, + `InputRichBlockButtons`, `RichBlockExpandableBlockQuotation`, `InputRichBlockExpandableBlockQuotation`, + `RichBlockDocument`, `InputRichBlockDocument`, `EphemeralMessageParameters`, `MessageGenerationStopped` and + `CommunityChatJoined` types. +- New #217: Add `disabled` field to `InlineKeyboardButton` type. +- New #217: Add `forceReply` field to `InlineKeyboardMarkup` and `ReplyKeyboardMarkup` types. +- New #217: Add `isCompact` field to `RichBlockTable` and `InputRichBlockTable` types. +- New #217: Add `canSendWelcomeMessages` field to `ChatAdministratorRights` and `ChatMemberAdministrator` types. +- New #217: Add `stoppedMessageGeneration` field to `Update` type. +- New #217: Add `communityChatJoined` field to `Message` type. +- New #217: Add `text`, `entities` and `isPrivate` fields to `UniqueGiftInfo` type. +- New #217: Add `ephemeralMessageParameters` parameter to `SendMessage`, `SendAnimation`, `SendAudio`, + `SendDocument`, `SendLivePhoto`, `SendPhoto`, `SendSticker`, `SendVideo`, `SendVideoNote`, `SendVoice`, + `SendContact`, `SendLocation`, `SendVenue` and `SendRichMessage` methods. +- New #217: Add `showCaptionAboveMedia` parameter to `EditEphemeralMessageCaption` method. +- New #217: Add `richMessage` parameter to `EditEphemeralMessageText` method. +- New #217: Add `canSendWelcomeMessages` parameter to `PromoteChatMember` method. +- New #217: Add `canStop` and `keepOnStop` parameters to `SendMessageDraft` and `SendRichMessageDraft` methods. +- Chg #217: Remove `receiverUserId` and `callbackQueryId` parameters from `SendMessage`, `SendAnimation`, + `SendAudio`, `SendDocument`, `SendPhoto`, `SendSticker`, `SendVideo`, `SendVideoNote`, `SendVoice`, + `SendContact`, `SendLocation` and `SendVenue` methods, use `ephemeralMessageParameters` parameter instead. +- Enh #217: Make `text` parameter of `EditEphemeralMessageText` method optional. - Enh #214: Rework `.gitattributes` to explicitly allow only distribution files for export. ## 0.21.2 July 15, 2026 From 8c708f6a11bf0f3fcf2747665baacb0f0c254b86 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:09:23 +0300 Subject: [PATCH 33/37] Make canSendWelcomeMessages field non-nullable --- src/Type/ChatAdministratorRights.php | 2 +- src/Type/ChatMemberAdministrator.php | 2 +- tests/Method/GetMyDefaultAdministratorRightsTest.php | 2 ++ tests/ParseResult/ValueProcessor/ChatMemberValueTest.php | 1 + tests/TelegramBotApi/TelegramBotApiTest.php | 2 ++ tests/Type/ChatAdministratorRightsTest.php | 3 ++- tests/Type/ChatMemberAdministratorTest.php | 2 +- 7 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Type/ChatAdministratorRights.php b/src/Type/ChatAdministratorRights.php index a9c99c84..6b86098f 100644 --- a/src/Type/ChatAdministratorRights.php +++ b/src/Type/ChatAdministratorRights.php @@ -29,7 +29,7 @@ public function __construct( public ?bool $canManageTopics = null, public ?bool $canManageDirectMessages = null, public ?bool $canManageTags = null, - public ?bool $canSendWelcomeMessages = null, + public bool $canSendWelcomeMessages = false, ) {} public function toRequestArray(): array diff --git a/src/Type/ChatMemberAdministrator.php b/src/Type/ChatMemberAdministrator.php index ea75df0c..5595edcf 100644 --- a/src/Type/ChatMemberAdministrator.php +++ b/src/Type/ChatMemberAdministrator.php @@ -32,7 +32,7 @@ public function __construct( public ?string $customTitle = null, public ?bool $canManageDirectMessages = null, public ?bool $canManageTags = null, - public ?bool $canSendWelcomeMessages = null, + public bool $canSendWelcomeMessages = false, ) {} public function getStatus(): string diff --git a/tests/Method/GetMyDefaultAdministratorRightsTest.php b/tests/Method/GetMyDefaultAdministratorRightsTest.php index 83a92b56..ee38df45 100644 --- a/tests/Method/GetMyDefaultAdministratorRightsTest.php +++ b/tests/Method/GetMyDefaultAdministratorRightsTest.php @@ -56,6 +56,7 @@ public function testPrepareResult(): void 'can_edit_messages' => true, 'can_pin_messages' => false, 'can_manage_topics' => true, + 'can_send_welcome_messages' => true, ])->call($method); assertTrue($preparedResult->isAnonymous); @@ -73,5 +74,6 @@ public function testPrepareResult(): void assertTrue($preparedResult->canEditMessages); assertFalse($preparedResult->canPinMessages); assertTrue($preparedResult->canManageTopics); + assertTrue($preparedResult->canSendWelcomeMessages); } } diff --git a/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php b/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php index c6007fbb..86c32cb6 100644 --- a/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php +++ b/tests/ParseResult/ValueProcessor/ChatMemberValueTest.php @@ -56,6 +56,7 @@ public static function dataBase(): array 'can_post_stories' => true, 'can_edit_stories' => true, 'can_delete_stories' => true, + 'can_send_welcome_messages' => true, ], ], [ diff --git a/tests/TelegramBotApi/TelegramBotApiTest.php b/tests/TelegramBotApi/TelegramBotApiTest.php index 950a7ed4..a7fc0b51 100644 --- a/tests/TelegramBotApi/TelegramBotApiTest.php +++ b/tests/TelegramBotApi/TelegramBotApiTest.php @@ -1412,6 +1412,7 @@ public function testGetMyDefaultAdministratorRights(): void 'can_edit_messages' => true, 'can_pin_messages' => false, 'can_manage_topics' => true, + 'can_send_welcome_messages' => true, ]); $result = $api->getMyDefaultAdministratorRights(); @@ -1431,6 +1432,7 @@ public function testGetMyDefaultAdministratorRights(): void assertTrue($result->canEditMessages); assertFalse($result->canPinMessages); assertTrue($result->canManageTopics); + assertTrue($result->canSendWelcomeMessages); } public function testGetMyDescription(): void diff --git a/tests/Type/ChatAdministratorRightsTest.php b/tests/Type/ChatAdministratorRightsTest.php index d72b52cc..b227d381 100644 --- a/tests/Type/ChatAdministratorRightsTest.php +++ b/tests/Type/ChatAdministratorRightsTest.php @@ -36,7 +36,7 @@ public function testBase(): void assertNull($rights->canManageTopics); assertNull($rights->canManageDirectMessages); assertNull($rights->canManageTags); - assertNull($rights->canSendWelcomeMessages); + assertFalse($rights->canSendWelcomeMessages); assertSame( [ @@ -51,6 +51,7 @@ public function testBase(): void 'can_post_stories' => true, 'can_edit_stories' => true, 'can_delete_stories' => false, + 'can_send_welcome_messages' => false, ], $rights->toRequestArray(), ); diff --git a/tests/Type/ChatMemberAdministratorTest.php b/tests/Type/ChatMemberAdministratorTest.php index a963aeb6..17fce47d 100644 --- a/tests/Type/ChatMemberAdministratorTest.php +++ b/tests/Type/ChatMemberAdministratorTest.php @@ -57,7 +57,7 @@ public function testBase(): void assertNull($member->customTitle); assertNull($member->canManageDirectMessages); assertNull($member->canManageTags); - assertNull($member->canSendWelcomeMessages); + assertFalse($member->canSendWelcomeMessages); } public function testFromTelegramResult(): void From ffec02b86df99487d6b4cb70b310ec21cbf537c0 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:10:44 +0300 Subject: [PATCH 34/37] Make canSendWelcomeMessages a required constructor parameter --- CHANGELOG.md | 2 ++ src/Type/ChatAdministratorRights.php | 2 +- src/Type/ChatMemberAdministrator.php | 2 +- tests/Method/SetMyDefaultAdministratorRightsTest.php | 2 +- tests/Type/ChatAdministratorRightsTest.php | 6 +++--- tests/Type/ChatMemberAdministratorTest.php | 3 ++- tests/Type/KeyboardButtonRequestChatTest.php | 2 ++ 7 files changed, 12 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d3c0b52..ef67b83d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ - New #217: Add `richMessage` parameter to `EditEphemeralMessageText` method. - New #217: Add `canSendWelcomeMessages` parameter to `PromoteChatMember` method. - New #217: Add `canStop` and `keepOnStop` parameters to `SendMessageDraft` and `SendRichMessageDraft` methods. +- Chg #217: Add required `canSendWelcomeMessages` constructor parameter to `ChatAdministratorRights` and + `ChatMemberAdministrator` types, placed after `canDeleteStories`. - Chg #217: Remove `receiverUserId` and `callbackQueryId` parameters from `SendMessage`, `SendAnimation`, `SendAudio`, `SendDocument`, `SendPhoto`, `SendSticker`, `SendVideo`, `SendVideoNote`, `SendVoice`, `SendContact`, `SendLocation` and `SendVenue` methods, use `ephemeralMessageParameters` parameter instead. diff --git a/src/Type/ChatAdministratorRights.php b/src/Type/ChatAdministratorRights.php index 6b86098f..5e938d35 100644 --- a/src/Type/ChatAdministratorRights.php +++ b/src/Type/ChatAdministratorRights.php @@ -23,13 +23,13 @@ public function __construct( public bool $canPostStories, public bool $canEditStories, public bool $canDeleteStories, + public bool $canSendWelcomeMessages, public ?bool $canPostMessages = null, public ?bool $canEditMessages = null, public ?bool $canPinMessages = null, public ?bool $canManageTopics = null, public ?bool $canManageDirectMessages = null, public ?bool $canManageTags = null, - public bool $canSendWelcomeMessages = false, ) {} public function toRequestArray(): array diff --git a/src/Type/ChatMemberAdministrator.php b/src/Type/ChatMemberAdministrator.php index 5595edcf..eb1d17bd 100644 --- a/src/Type/ChatMemberAdministrator.php +++ b/src/Type/ChatMemberAdministrator.php @@ -25,6 +25,7 @@ public function __construct( public bool $canPostStories, public bool $canEditStories, public bool $canDeleteStories, + public bool $canSendWelcomeMessages, public ?bool $canPostMessages = null, public ?bool $canEditMessages = null, public ?bool $canPinMessages = null, @@ -32,7 +33,6 @@ public function __construct( public ?string $customTitle = null, public ?bool $canManageDirectMessages = null, public ?bool $canManageTags = null, - public bool $canSendWelcomeMessages = false, ) {} public function getStatus(): string diff --git a/tests/Method/SetMyDefaultAdministratorRightsTest.php b/tests/Method/SetMyDefaultAdministratorRightsTest.php index 25258fe1..4295f0ac 100644 --- a/tests/Method/SetMyDefaultAdministratorRightsTest.php +++ b/tests/Method/SetMyDefaultAdministratorRightsTest.php @@ -26,7 +26,7 @@ public function testBase(): void public function testFull(): void { - $rights = new ChatAdministratorRights(true, false, true, true, true, true, true, true, true, true, true); + $rights = new ChatAdministratorRights(true, false, true, true, true, true, true, true, true, true, true, false); $method = new SetMyDefaultAdministratorRights($rights, false); assertSame( diff --git a/tests/Type/ChatAdministratorRightsTest.php b/tests/Type/ChatAdministratorRightsTest.php index b227d381..b351b398 100644 --- a/tests/Type/ChatAdministratorRightsTest.php +++ b/tests/Type/ChatAdministratorRightsTest.php @@ -17,7 +17,7 @@ final class ChatAdministratorRightsTest extends TestCase { public function testBase(): void { - $rights = new ChatAdministratorRights(true, true, true, true, true, true, true, true, true, true, false); + $rights = new ChatAdministratorRights(true, true, true, true, true, true, true, true, true, true, false, true); assertTrue($rights->isAnonymous); assertTrue($rights->canManageChat); @@ -36,7 +36,7 @@ public function testBase(): void assertNull($rights->canManageTopics); assertNull($rights->canManageDirectMessages); assertNull($rights->canManageTags); - assertFalse($rights->canSendWelcomeMessages); + assertTrue($rights->canSendWelcomeMessages); assertSame( [ @@ -51,7 +51,7 @@ public function testBase(): void 'can_post_stories' => true, 'can_edit_stories' => true, 'can_delete_stories' => false, - 'can_send_welcome_messages' => false, + 'can_send_welcome_messages' => true, ], $rights->toRequestArray(), ); diff --git a/tests/Type/ChatMemberAdministratorTest.php b/tests/Type/ChatMemberAdministratorTest.php index 17fce47d..fb14f7b0 100644 --- a/tests/Type/ChatMemberAdministratorTest.php +++ b/tests/Type/ChatMemberAdministratorTest.php @@ -33,6 +33,7 @@ public function testBase(): void true, true, true, + true, ); assertSame('administrator', $member->getStatus()); @@ -57,7 +58,7 @@ public function testBase(): void assertNull($member->customTitle); assertNull($member->canManageDirectMessages); assertNull($member->canManageTags); - assertFalse($member->canSendWelcomeMessages); + assertTrue($member->canSendWelcomeMessages); } public function testFromTelegramResult(): void diff --git a/tests/Type/KeyboardButtonRequestChatTest.php b/tests/Type/KeyboardButtonRequestChatTest.php index 791ee238..195673a4 100644 --- a/tests/Type/KeyboardButtonRequestChatTest.php +++ b/tests/Type/KeyboardButtonRequestChatTest.php @@ -54,6 +54,7 @@ public function testFilled(): void true, true, true, + true, ); $botAdministratorRights = new ChatAdministratorRights( true, @@ -67,6 +68,7 @@ public function testFilled(): void true, true, true, + true, ); $keyboardButtonRequestChat = new KeyboardButtonRequestChat( 1, From 3c9eb794bce71ee0dc6473eb45dcc75e2d8766ec Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 11:23:58 +0300 Subject: [PATCH 35/37] Restrict testFromTelegramResult cases to types the API returns --- AGENTS.md | 26 +++++++++++++++++----- tests/Type/RichTextButtonTest.php | 14 ------------ tests/Type/SuggestedPostParametersTest.php | 20 ----------------- 3 files changed, 21 insertions(+), 39 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d762c155..8d648261 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -200,7 +200,18 @@ The return type is `FailResult|`. and `testFull()` is the same object with **every** optional argument passed. Do not invent other names; a handful of old tests use `testFilled()` or `testFromTelegramResultMinimal()`, they are not the convention. -- Result types — `tests/Type/Test.php`. Without optional fields, two cases: +- **`testFromTelegramResult()` / `testFromTelegramResultFull()` belong only to types the API can + actually return.** Living in `src/Type` and being parseable by `ObjectFactory` is not the + criterion: a type the bot only ever *sends* never arrives as a result, so an `ObjectFactory` case + for it asserts behaviour the library never performs. Decide the role from the Bot API reference: + + | The type appears … | Test cases | + | --- | --- | + | only as a field of returned objects | properties are asserted; `FromTelegramResult` cases present | + | only as a method parameter, or as a field of another sent-only type | `toRequestArray()` is asserted; **no** `FromTelegramResult` case | + | in both roles | both — properties *and* `toRequestArray()`, plus the `FromTelegramResult` cases | + +- Returned types — `tests/Type/Test.php`. Without optional fields, two cases: ```php final class CommunityTest extends TestCase { @@ -235,10 +246,14 @@ The return type is `FailResult|`. In the `FromTelegramResult` cases nested objects are checked with `assertInstanceOf()` plus a couple of their fields — see `tests/Type/RichBlockAnimationTest.php`. -- Input types — same `testBase()`/`testFull()` split, but each case asserts the exact - `toRequestArray()` array instead of the properties. A type that accepts an `InputFile` gets one +- Sent-only types — same `testBase()`/`testFull()` split, but each case asserts the exact + `toRequestArray()` array instead of the properties, and there is no `FromTelegramResult` case at + all — see `tests/Type/SuggestedPostParametersTest.php`. A type that accepts an `InputFile` gets one more case, `testFileCollectorIsPropagated()`, which passes a `FileCollector`, expects `attach://file0` in the array and asserts `$fileCollector->get()`. +- Dual types — the four cases of a returned type, with `testBase()` and `testFull()` additionally + asserting `toRequestArray()`; no extra case is added for the serialization. See + `tests/Type/RichMessageButtonTest.php`. - Methods — `tests/Method/Test.php`: `testBase()` asserts `getHttpMethod()`, `getApiMethod()` and the exact `getData()` array; `testFull()` repeats it with every optional parameter passed (keys in `getData()` order, not in constructor order); `testPrepareResult()` runs the method @@ -247,8 +262,9 @@ The return type is `FailResult|`. placed, using `TestHelper::createSuccessStubApi(...)`: build the stub, call the method, assert the result. Optional parameters are not repeated here — they are already covered by the method test. - New fields on existing types extend the existing test of that type rather than adding a new file: - the new field goes into `testFull()` and `testFromTelegramResultFull()`, and into `testBase()` / - `testFromTelegramResult()` as a `null` assertion. + the new field goes into `testFull()` and, if the type has them, `testFromTelegramResultFull()`, and + into `testBase()` / `testFromTelegramResult()` as a `null` assertion. Do not add a + `FromTelegramResult` case to a sent-only type just because it gained a field. ## Preserving Backward Compatibility diff --git a/tests/Type/RichTextButtonTest.php b/tests/Type/RichTextButtonTest.php index 503672a2..d1ef78cf 100644 --- a/tests/Type/RichTextButtonTest.php +++ b/tests/Type/RichTextButtonTest.php @@ -6,7 +6,6 @@ use PHPUnit\Framework\TestCase; use Phptg\BotApi\ParseResult\ObjectFactory; -use Phptg\BotApi\Type\RichBlockParagraph; use Phptg\BotApi\Type\RichMessageButton; use Phptg\BotApi\Type\RichTextButton; @@ -49,17 +48,4 @@ public function testFromTelegramResult(): void assertSame('test', $richText->button->text); assertSame('data', $richText->button->callbackData); } - - public function testFromTelegramResultViaRichTextValue(): void - { - $block = (new ObjectFactory())->create([ - 'type' => 'paragraph', - 'text' => [ - 'type' => 'button', - 'button' => ['text' => 'test'], - ], - ], null, RichBlockParagraph::class); - - assertInstanceOf(RichTextButton::class, $block->text); - } } diff --git a/tests/Type/SuggestedPostParametersTest.php b/tests/Type/SuggestedPostParametersTest.php index 15357f9c..af83294c 100644 --- a/tests/Type/SuggestedPostParametersTest.php +++ b/tests/Type/SuggestedPostParametersTest.php @@ -5,7 +5,6 @@ namespace Phptg\BotApi\Tests\Type; use PHPUnit\Framework\TestCase; -use Phptg\BotApi\ParseResult\ObjectFactory; use Phptg\BotApi\Type\SuggestedPostParameters; use Phptg\BotApi\Type\SuggestedPostPrice; @@ -41,23 +40,4 @@ public function testFull(): void $parameters->toRequestArray(), ); } - - public function testFromTelegramResult(): void - { - $parameters = (new ObjectFactory())->create( - [ - 'price' => [ - 'currency' => 'XTR', - 'amount' => 100, - ], - 'send_date' => 1620000300, - ], - null, - SuggestedPostParameters::class, - ); - - assertSame('XTR', $parameters->price?->currency); - assertSame(100, $parameters->price?->amount); - assertSame(1620000300, $parameters->sendDate); - } } From 22e78fea6a1e60721482fe0387c74b74b1f2501c Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 12:21:35 +0300 Subject: [PATCH 36/37] fix --- CHANGELOG.md | 4 +--- tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef67b83d..a229be4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Telegram Bot API for PHP Change Log -## 0.21.3 under development +## 0.22 August 25, 2026 - New #217: Add `DisabledButton`, `RichMessageButton`, `RichTextButton`, `RichBlockButtons`, `InputRichBlockButtons`, `RichBlockExpandableBlockQuotation`, `InputRichBlockExpandableBlockQuotation`, @@ -20,8 +20,6 @@ - New #217: Add `richMessage` parameter to `EditEphemeralMessageText` method. - New #217: Add `canSendWelcomeMessages` parameter to `PromoteChatMember` method. - New #217: Add `canStop` and `keepOnStop` parameters to `SendMessageDraft` and `SendRichMessageDraft` methods. -- Chg #217: Add required `canSendWelcomeMessages` constructor parameter to `ChatAdministratorRights` and - `ChatMemberAdministrator` types, placed after `canDeleteStories`. - Chg #217: Remove `receiverUserId` and `callbackQueryId` parameters from `SendMessage`, `SendAnimation`, `SendAudio`, `SendDocument`, `SendPhoto`, `SendSticker`, `SendVideo`, `SendVideoNote`, `SendVoice`, `SendContact`, `SendLocation` and `SendVenue` methods, use `ephemeralMessageParameters` parameter instead. diff --git a/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php b/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php index 7d1adf03..17351be3 100644 --- a/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php +++ b/tests/Method/UpdatingMessage/EditEphemeralMessageTextTest.php @@ -22,7 +22,7 @@ final class EditEphemeralMessageTextTest extends TestCase { public function testBase(): void { - $method = new EditEphemeralMessageText(23, 45, 34, 'hello'); + $method = new EditEphemeralMessageText(23, 45, 34); assertSame(HttpMethod::POST, $method->getHttpMethod()); assertSame('editEphemeralMessageText', $method->getApiMethod()); @@ -31,7 +31,6 @@ public function testBase(): void 'chat_id' => 23, 'receiver_user_id' => 45, 'ephemeral_message_id' => 34, - 'text' => 'hello', ], $method->getData(), ); From 09f93b82b8a5606975480f451b30738545376d18 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Aug 2026 12:28:51 +0300 Subject: [PATCH 37/37] fix rector --- rector.php | 7 ------- tools/rector/composer.json | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/rector.php b/rector.php index 750489bd..d70822f9 100644 --- a/rector.php +++ b/rector.php @@ -14,14 +14,7 @@ __DIR__ . '/tests', ]) ->withPhpSets(php82: true) - ->withConfiguredRule( - AddSensitiveParameterAttributeRector::class, - [ - 'sensitive_parameters' => ['token', 'providerToken', 'secretToken'], - ] - ) ->withSkip([ ClosureToArrowFunctionRector::class, - NullToStrictStringFuncCallArgRector::class, ClassPropertyAssignToConstructorPromotionRector::class, ]); diff --git a/tools/rector/composer.json b/tools/rector/composer.json index 9beb40da..2c007d47 100644 --- a/tools/rector/composer.json +++ b/tools/rector/composer.json @@ -1,5 +1,5 @@ { "require-dev": { - "rector/rector": "^2.4.2" + "rector/rector": "^2.6.3" } }