Fix Telegram bot parsing of inline keyboard (#146376)

* bug fix for inline keyboard

* update inline keyboard test

* Update tests/components/telegram_bot/test_telegram_bot.py

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>

* revert last_message_id and updated tests

* removed TypeError test

---------

Co-authored-by: Martin Hjelmare <marhje52@gmail.com>
This commit is contained in:
hanwg 2025-06-21 09:21:10 +08:00 committed by GitHub
parent 2e5de732a7
commit 7442f7af28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 127 additions and 7 deletions

View File

@ -321,8 +321,8 @@ class TelegramNotificationService:
for key in row_keyboard.split(","):
if ":/" in key:
# check if command or URL
if key.startswith("https://"):
label = key.split(",")[0]
if "https://" in key:
label = key.split(":")[0]
url = key[len(label) + 1 :]
buttons.append(InlineKeyboardButton(label, url=url))
else:

View File

@ -1,12 +1,14 @@
"""Tests for the telegram_bot component."""
import base64
from datetime import datetime
import io
from typing import Any
from unittest.mock import AsyncMock, MagicMock, mock_open, patch
import pytest
from telegram import Update
from telegram import Chat, InlineKeyboardButton, InlineKeyboardMarkup, Message, Update
from telegram.constants import ChatType, ParseMode
from telegram.error import (
InvalidToken,
NetworkError,
@ -16,28 +18,37 @@ from telegram.error import (
)
from homeassistant.components.telegram_bot import (
ATTR_LATITUDE,
ATTR_LONGITUDE,
async_setup_entry,
)
from homeassistant.components.telegram_bot.const import (
ATTR_AUTHENTICATION,
ATTR_CALLBACK_QUERY_ID,
ATTR_CAPTION,
ATTR_CHAT_ID,
ATTR_DISABLE_NOTIF,
ATTR_DISABLE_WEB_PREV,
ATTR_FILE,
ATTR_KEYBOARD,
ATTR_KEYBOARD_INLINE,
ATTR_LATITUDE,
ATTR_LONGITUDE,
ATTR_MESSAGE,
ATTR_MESSAGE_TAG,
ATTR_MESSAGE_THREAD_ID,
ATTR_MESSAGEID,
ATTR_OPTIONS,
ATTR_PARSER,
ATTR_PASSWORD,
ATTR_QUESTION,
ATTR_REPLY_TO_MSGID,
ATTR_SHOW_ALERT,
ATTR_STICKER_ID,
ATTR_TARGET,
ATTR_TIMEOUT,
ATTR_URL,
ATTR_USERNAME,
ATTR_VERIFY_SSL,
CONF_CONFIG_ENTRY_ID,
CONF_PLATFORM,
DOMAIN,
PLATFORM_BROADCAST,
SERVICE_ANSWER_CALLBACK_QUERY,
@ -55,12 +66,12 @@ from homeassistant.components.telegram_bot import (
SERVICE_SEND_STICKER,
SERVICE_SEND_VIDEO,
SERVICE_SEND_VOICE,
async_setup_entry,
)
from homeassistant.components.telegram_bot.webhooks import TELEGRAM_WEBHOOK_URL
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import (
CONF_API_KEY,
CONF_PLATFORM,
HTTP_BASIC_AUTHENTICATION,
HTTP_BEARER_AUTHENTICATION,
HTTP_DIGEST_AUTHENTICATION,
@ -96,6 +107,26 @@ async def test_polling_platform_init(hass: HomeAssistant, polling_platform) -> N
SERVICE_SEND_MESSAGE,
{ATTR_MESSAGE: "test_message", ATTR_MESSAGE_THREAD_ID: "123"},
),
(
SERVICE_SEND_MESSAGE,
{
ATTR_KEYBOARD: ["/command1, /command2", "/command3"],
ATTR_MESSAGE: "test_message",
ATTR_PARSER: ParseMode.HTML,
ATTR_TIMEOUT: 15,
ATTR_DISABLE_NOTIF: True,
ATTR_DISABLE_WEB_PREV: True,
ATTR_MESSAGE_TAG: "mock_tag",
ATTR_REPLY_TO_MSGID: 12345,
},
),
(
SERVICE_SEND_MESSAGE,
{
ATTR_KEYBOARD: [],
ATTR_MESSAGE: "test_message",
},
),
(
SERVICE_SEND_STICKER,
{
@ -145,6 +176,95 @@ async def test_send_message(
assert (response["chats"][0]["message_id"]) == 12345
@pytest.mark.parametrize(
("input", "expected"),
[
(
{
ATTR_MESSAGE: "test_message",
ATTR_KEYBOARD_INLINE: "command1:/cmd1,/cmd2,mock_link:https://mock_link",
},
InlineKeyboardMarkup(
# 1 row with 3 buttons
[
[
InlineKeyboardButton(callback_data="/cmd1", text="command1"),
InlineKeyboardButton(callback_data="/cmd2", text="CMD2"),
InlineKeyboardButton(url="https://mock_link", text="mock_link"),
]
]
),
),
(
{
ATTR_MESSAGE: "test_message",
ATTR_KEYBOARD_INLINE: [
[["command1", "/cmd1"]],
[["mock_link", "https://mock_link"]],
],
},
InlineKeyboardMarkup(
# 2 rows each with 1 button
[
[InlineKeyboardButton(callback_data="/cmd1", text="command1")],
[InlineKeyboardButton(url="https://mock_link", text="mock_link")],
]
),
),
],
)
async def test_send_message_with_inline_keyboard(
hass: HomeAssistant,
webhook_platform,
input: dict[str, Any],
expected: InlineKeyboardMarkup,
) -> None:
"""Test the send_message service.
Tests any service that does not require files to be sent.
"""
context = Context()
events = async_capture_events(hass, "telegram_sent")
with patch(
"homeassistant.components.telegram_bot.bot.Bot.send_message",
AsyncMock(
return_value=Message(
message_id=12345,
date=datetime.now(),
chat=Chat(id=123456, type=ChatType.PRIVATE),
)
),
) as mock_send_message:
response = await hass.services.async_call(
DOMAIN,
SERVICE_SEND_MESSAGE,
input,
blocking=True,
context=context,
return_response=True,
)
await hass.async_block_till_done()
mock_send_message.assert_called_once_with(
12345678,
"test_message",
parse_mode=ParseMode.MARKDOWN,
disable_web_page_preview=None,
disable_notification=False,
reply_to_message_id=None,
reply_markup=expected,
read_timeout=None,
message_thread_id=None,
)
assert len(events) == 1
assert events[0].context == context
assert len(response["chats"]) == 1
assert (response["chats"][0]["message_id"]) == 12345
@patch(
"builtins.open",
mock_open(