Add Telegram Bot message reactions (#146354)

This commit is contained in:
Aviad Levy 2025-06-16 16:48:59 +03:00 committed by GitHub
parent 664441eaec
commit cce878213f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 161 additions and 4 deletions

View File

@ -46,6 +46,7 @@ from .const import (
ATTR_DISABLE_WEB_PREV,
ATTR_FILE,
ATTR_IS_ANONYMOUS,
ATTR_IS_BIG,
ATTR_KEYBOARD,
ATTR_KEYBOARD_INLINE,
ATTR_MESSAGE,
@ -58,6 +59,7 @@ from .const import (
ATTR_PARSER,
ATTR_PASSWORD,
ATTR_QUESTION,
ATTR_REACTION,
ATTR_RESIZE_KEYBOARD,
ATTR_SHOW_ALERT,
ATTR_STICKER_ID,
@ -94,6 +96,7 @@ from .const import (
SERVICE_SEND_STICKER,
SERVICE_SEND_VIDEO,
SERVICE_SEND_VOICE,
SERVICE_SET_MESSAGE_REACTION,
)
_LOGGER = logging.getLogger(__name__)
@ -250,6 +253,19 @@ SERVICE_SCHEMA_LEAVE_CHAT = vol.Schema(
}
)
SERVICE_SCHEMA_SET_MESSAGE_REACTION = vol.Schema(
{
vol.Optional(CONF_CONFIG_ENTRY_ID): cv.string,
vol.Required(ATTR_MESSAGEID): vol.Any(
cv.positive_int, vol.All(cv.string, "last")
),
vol.Required(ATTR_CHAT_ID): vol.Coerce(int),
vol.Required(ATTR_REACTION): cv.string,
vol.Optional(ATTR_IS_BIG, default=False): cv.boolean,
},
extra=vol.ALLOW_EXTRA,
)
SERVICE_MAP = {
SERVICE_SEND_MESSAGE: SERVICE_SCHEMA_SEND_MESSAGE,
SERVICE_SEND_PHOTO: SERVICE_SCHEMA_SEND_FILE,
@ -266,6 +282,7 @@ SERVICE_MAP = {
SERVICE_ANSWER_CALLBACK_QUERY: SERVICE_SCHEMA_ANSWER_CALLBACK_QUERY,
SERVICE_DELETE_MESSAGE: SERVICE_SCHEMA_DELETE_MESSAGE,
SERVICE_LEAVE_CHAT: SERVICE_SCHEMA_LEAVE_CHAT,
SERVICE_SET_MESSAGE_REACTION: SERVICE_SCHEMA_SET_MESSAGE_REACTION,
}
@ -378,6 +395,8 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
messages = await notify_service.leave_chat(
context=service.context, **kwargs
)
elif msgtype == SERVICE_SET_MESSAGE_REACTION:
await notify_service.set_message_reaction(context=service.context, **kwargs)
else:
await notify_service.edit_message(
msgtype, context=service.context, **kwargs

View File

@ -786,6 +786,39 @@ class TelegramNotificationService:
self.bot.leave_chat, "Error leaving chat", None, chat_id, context=context
)
async def set_message_reaction(
self,
chat_id: int,
reaction: str,
is_big: bool = False,
context: Context | None = None,
**kwargs,
) -> None:
"""Set the bot's reaction for a given message."""
chat_id = self._get_target_chat_ids(chat_id)[0]
message_id, _ = self._get_msg_ids(kwargs, chat_id)
params = self._get_msg_kwargs(kwargs)
_LOGGER.debug(
"Set reaction to message %s in chat ID %s to %s with params: %s",
message_id,
chat_id,
reaction,
params,
)
await self._send_msg(
self.bot.set_message_reaction,
"Error setting message reaction",
params[ATTR_MESSAGE_TAG],
chat_id,
message_id,
reaction=reaction,
is_big=is_big,
read_timeout=params[ATTR_TIMEOUT],
context=context,
)
def initialize_bot(hass: HomeAssistant, p_config: MappingProxyType[str, Any]) -> Bot:
"""Initialize telegram bot with proxy support."""

View File

@ -43,6 +43,7 @@ SERVICE_SEND_VOICE = "send_voice"
SERVICE_SEND_DOCUMENT = "send_document"
SERVICE_SEND_LOCATION = "send_location"
SERVICE_SEND_POLL = "send_poll"
SERVICE_SET_MESSAGE_REACTION = "set_message_reaction"
SERVICE_EDIT_MESSAGE = "edit_message"
SERVICE_EDIT_CAPTION = "edit_caption"
SERVICE_EDIT_REPLYMARKUP = "edit_replymarkup"
@ -87,6 +88,8 @@ ATTR_MSG = "message"
ATTR_MSGID = "id"
ATTR_PARSER = "parse_mode"
ATTR_PASSWORD = "password"
ATTR_REACTION = "reaction"
ATTR_IS_BIG = "is_big"
ATTR_REPLY_TO_MSGID = "reply_to_message_id"
ATTR_REPLYMARKUP = "reply_markup"
ATTR_SHOW_ALERT = "show_alert"

View File

@ -44,6 +44,9 @@
},
"leave_chat": {
"service": "mdi:exit-run"
},
"set_message_reaction": {
"service": "mdi:emoticon-happy"
}
}
}

View File

@ -787,3 +787,29 @@ leave_chat:
example: 12345
selector:
text:
set_message_reaction:
fields:
config_entry_id:
selector:
config_entry:
integration: telegram_bot
message_id:
required: true
example: 54321
selector:
text:
chat_id:
required: true
example: 12345
selector:
text:
reaction:
required: true
example: 👍
selector:
text:
is_big:
required: false
selector:
boolean:

View File

@ -857,6 +857,32 @@
"description": "Chat ID of the group from which the bot should be removed."
}
}
},
"set_message_reaction": {
"name": "Set message reaction",
"description": "Sets the bot's reaction for a given message.",
"fields": {
"config_entry_id": {
"name": "[%key:component::telegram_bot::services::send_message::fields::config_entry_id::name%]",
"description": "The config entry representing the Telegram bot to set the message reaction."
},
"message_id": {
"name": "[%key:component::telegram_bot::services::edit_message::fields::message_id::name%]",
"description": "ID of the message to react to."
},
"chat_id": {
"name": "[%key:component::telegram_bot::services::edit_message::fields::chat_id::name%]",
"description": "ID of the chat containing the message."
},
"reaction": {
"name": "Reaction",
"description": "Emoji reaction to use."
},
"is_big": {
"name": "Large animation",
"description": "Whether the reaction animation should be large."
}
}
}
},
"exceptions": {

View File

@ -30,6 +30,7 @@ from homeassistant.components.telegram_bot import (
ATTR_OPTIONS,
ATTR_PASSWORD,
ATTR_QUESTION,
ATTR_SHOW_ALERT,
ATTR_STICKER_ID,
ATTR_TARGET,
ATTR_URL,
@ -752,20 +753,27 @@ async def test_answer_callback_query(
await hass.async_block_till_done()
with patch(
"homeassistant.components.telegram_bot.bot.TelegramNotificationService.answer_callback_query"
"homeassistant.components.telegram_bot.bot.Bot.answer_callback_query"
) as mock:
await hass.services.async_call(
DOMAIN,
SERVICE_ANSWER_CALLBACK_QUERY,
{
ATTR_MESSAGE: "mock message",
ATTR_CALLBACK_QUERY_ID: 12345,
ATTR_CALLBACK_QUERY_ID: 123456,
ATTR_SHOW_ALERT: True,
},
blocking=True,
)
await hass.async_block_till_done()
mock.assert_called_once()
mock.assert_called_with(
123456,
text="mock message",
show_alert=True,
read_timeout=None,
)
async def test_leave_chat(
@ -779,20 +787,23 @@ async def test_leave_chat(
await hass.async_block_till_done()
with patch(
"homeassistant.components.telegram_bot.bot.TelegramNotificationService.leave_chat",
"homeassistant.components.telegram_bot.bot.Bot.leave_chat",
AsyncMock(return_value=True),
) as mock:
await hass.services.async_call(
DOMAIN,
SERVICE_LEAVE_CHAT,
{
ATTR_CHAT_ID: 12345,
ATTR_CHAT_ID: 123456,
},
blocking=True,
)
await hass.async_block_till_done()
mock.assert_called_once()
mock.assert_called_with(
123456,
)
async def test_send_video(
@ -974,3 +985,39 @@ async def test_send_video(
await hass.async_block_till_done()
assert mock_get.call_count > 0
assert response["chats"][0]["message_id"] == 12345
async def test_set_message_reaction(
hass: HomeAssistant,
mock_broadcast_config_entry: MockConfigEntry,
mock_external_calls: None,
) -> None:
"""Test set message reaction."""
mock_broadcast_config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(mock_broadcast_config_entry.entry_id)
await hass.async_block_till_done()
with patch(
"homeassistant.components.telegram_bot.bot.Bot.set_message_reaction",
AsyncMock(return_value=True),
) as mock:
await hass.services.async_call(
DOMAIN,
"set_message_reaction",
{
ATTR_CHAT_ID: 123456,
ATTR_MESSAGEID: 54321,
"reaction": "👍",
"is_big": True,
},
blocking=True,
)
await hass.async_block_till_done()
mock.assert_called_once_with(
123456,
54321,
reaction="👍",
is_big=True,
read_timeout=None,
)