Отправка без кнопок больше не падает на reply_markup

При пустом списке кнопок в Bot API уходило поле reply_markup: null, а Telegram ждёт объект или отсутствие поля. Теперь поле не передаётся, если кнопок нет — и для текста, и для фото.
This commit is contained in:
MpakobecEx 2026-10-26 00:31:27 +05:00
parent 5c6c49685a
commit 299a1e9893
5 changed files with 67 additions and 23 deletions

View File

@ -171,7 +171,7 @@ make backup E=test # на домашнем сервере: ./scripts/clickho
## Версионирование ## Версионирование
- Единый источник версии — `common/version.py` (`__version__`), текущая — `0.6.2`. - Единый источник версии — `common/version.py` (`__version__`), текущая — `0.6.3`.
- Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`). - Схема — SemVer-подобная (`MAJOR.MINOR.PATCH`); git-тег = `v` + версия (`v0.1`).
- Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3). - Docker-образы тегируются версией (Блок 2), события ClickHouse получают `bot_version` (Блок 3).
- Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`. - Выпуск: изменить `common/version.py` → коммит → аннотированный тег `vX.Y` → `git push --tags`.

View File

@ -123,16 +123,14 @@ async def send_to_chat(
markup = {"inline_keyboard": rows} if rows else None markup = {"inline_keyboard": rows} if rows else None
if photo is None: if photo is None:
await _call( payload = {
settings,
"sendMessage",
{
"chat_id": settings.chat_id, "chat_id": settings.chat_id,
"text": text, "text": text,
"parse_mode": "HTML", "parse_mode": "HTML",
"reply_markup": markup, }
}, if markup:
) payload["reply_markup"] = markup
await _call(settings, "sendMessage", payload)
return return
data, mime, filename = photo data, mime, filename = photo
@ -146,13 +144,11 @@ async def send_to_chat(
await _call_multipart(settings, "sendPhoto", fields, {"photo": (filename, data, mime)}) await _call_multipart(settings, "sendPhoto", fields, {"photo": (filename, data, mime)})
if text and not caption: if text and not caption:
await _call( payload = {
settings,
"sendMessage",
{
"chat_id": settings.chat_id, "chat_id": settings.chat_id,
"text": text, "text": text,
"parse_mode": "HTML", "parse_mode": "HTML",
"reply_markup": markup, }
}, if markup:
) payload["reply_markup"] = markup
await _call(settings, "sendMessage", payload)

View File

@ -7,4 +7,4 @@
from __future__ import annotations from __future__ import annotations
__version__ = "0.6.2" __version__ = "0.6.3"

View File

@ -8,6 +8,7 @@ import pytest
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from admin import telegram from admin import telegram
from common.config import AdminSettings
from common.repositories import admin_messages_repo from common.repositories import admin_messages_repo
@ -116,3 +117,50 @@ def test_send_telegram_error(
assert "chat not found" in response.text assert "chat not found" in response.text
assert fake_send["audit"]["success"] is False assert fake_send["audit"]["success"] is False
assert fake_send["audit"]["error"] == "chat not found" assert fake_send["audit"]["error"] == "chat not found"
async def test_telegram_omits_reply_markup_without_buttons(monkeypatch: pytest.MonkeyPatch) -> None:
calls: list[dict[str, Any]] = []
async def fake_call(settings: Any, method: str, payload: dict[str, Any]) -> None:
calls.append(payload)
monkeypatch.setattr(telegram, "_call", fake_call)
settings = AdminSettings(_env_file=None, bot_token="1:a", chat_id=-100)
await telegram.send_to_chat(settings, text="Привет", buttons=[], photo=None)
assert "reply_markup" not in calls[0]
async def test_telegram_adds_reply_markup_with_buttons(monkeypatch: pytest.MonkeyPatch) -> None:
calls: list[dict[str, Any]] = []
async def fake_call(settings: Any, method: str, payload: dict[str, Any]) -> None:
calls.append(payload)
monkeypatch.setattr(telegram, "_call", fake_call)
settings = AdminSettings(_env_file=None, bot_token="1:a", chat_id=-100)
await telegram.send_to_chat(settings, text="Привет", buttons=[("Канал", "https://t.me/x")], photo=None)
assert calls[0]["reply_markup"]["inline_keyboard"][0][0]["url"] == "https://t.me/x"
async def test_telegram_photo_omits_reply_markup_without_buttons(monkeypatch: pytest.MonkeyPatch) -> None:
fields: list[dict[str, str]] = []
async def fake_multipart(settings: Any, method: str, data: dict[str, str], files: dict[str, Any]) -> None:
fields.append(data)
monkeypatch.setattr(telegram, "_call_multipart", fake_multipart)
settings = AdminSettings(_env_file=None, bot_token="1:a", chat_id=-100)
await telegram.send_to_chat(
settings,
text="Подпись",
buttons=[],
photo=(b"data", "image/png", "pic.png"),
)
assert "reply_markup" not in fields[0]

View File

@ -7,4 +7,4 @@ from common import __version__
def test_version_is_set(): def test_version_is_set():
assert __version__ assert __version__
assert __version__ == "0.6.2" assert __version__ == "0.6.3"