mirror of
https://github.com/home-assistant/core.git
synced 2026-04-20 00:19:02 +02:00
Add send_message_draft action to telegram_bot (#165682)
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -62,6 +62,7 @@ from .const import (
|
||||
ATTR_DIRECTORY_PATH,
|
||||
ATTR_DISABLE_NOTIF,
|
||||
ATTR_DISABLE_WEB_PREV,
|
||||
ATTR_DRAFT_ID,
|
||||
ATTR_FILE,
|
||||
ATTR_FILE_ID,
|
||||
ATTR_FILE_NAME,
|
||||
@@ -129,6 +130,7 @@ from .const import (
|
||||
SERVICE_SEND_LOCATION,
|
||||
SERVICE_SEND_MEDIA_GROUP,
|
||||
SERVICE_SEND_MESSAGE,
|
||||
SERVICE_SEND_MESSAGE_DRAFT,
|
||||
SERVICE_SEND_PHOTO,
|
||||
SERVICE_SEND_POLL,
|
||||
SERVICE_SEND_STICKER,
|
||||
@@ -176,6 +178,19 @@ SERVICE_SCHEMA_SEND_MESSAGE = vol.All(
|
||||
),
|
||||
)
|
||||
|
||||
SERVICE_SCHEMA_SEND_MESSAGE_DRAFT = vol.Schema(
|
||||
{
|
||||
vol.Optional(ATTR_ENTITY_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(ATTR_TARGET): vol.All(cv.ensure_list, [vol.Coerce(int)]),
|
||||
vol.Optional(CONF_CONFIG_ENTRY_ID): cv.string,
|
||||
vol.Optional(ATTR_CHAT_ID): vol.All(cv.ensure_list, [vol.Coerce(int)]),
|
||||
vol.Optional(ATTR_MESSAGE_THREAD_ID): vol.Coerce(int),
|
||||
vol.Required(ATTR_DRAFT_ID): vol.All(vol.Coerce(int), vol.Range(min=1)),
|
||||
vol.Required(ATTR_MESSAGE): cv.string,
|
||||
vol.Optional(ATTR_PARSER): ATTR_PARSER_SCHEMA,
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_SCHEMA_SEND_CHAT_ACTION = vol.All(
|
||||
cv.deprecated(ATTR_TIMEOUT),
|
||||
vol.Schema(
|
||||
@@ -424,6 +439,7 @@ SERVICE_SCHEMA_DOWNLOAD_FILE = vol.Schema(
|
||||
|
||||
SERVICE_MAP: dict[str, VolSchemaType] = {
|
||||
SERVICE_SEND_MESSAGE: SERVICE_SCHEMA_SEND_MESSAGE,
|
||||
SERVICE_SEND_MESSAGE_DRAFT: SERVICE_SCHEMA_SEND_MESSAGE_DRAFT,
|
||||
SERVICE_SEND_CHAT_ACTION: SERVICE_SCHEMA_SEND_CHAT_ACTION,
|
||||
SERVICE_SEND_PHOTO: SERVICE_SCHEMA_SEND_FILE,
|
||||
SERVICE_SEND_MEDIA_GROUP: SERVICE_SCHEMA_SEND_MEDIA_GROUP,
|
||||
@@ -615,6 +631,8 @@ async def _call_service(
|
||||
await notify_service.set_message_reaction(context=service.context, **kwargs)
|
||||
elif service_name == SERVICE_EDIT_MESSAGE_MEDIA:
|
||||
await notify_service.edit_message_media(context=service.context, **kwargs)
|
||||
elif service_name == SERVICE_SEND_MESSAGE_DRAFT:
|
||||
await notify_service.send_message_draft(context=service.context, **kwargs)
|
||||
elif service_name == SERVICE_DOWNLOAD_FILE:
|
||||
return await notify_service.download_file(context=service.context, **kwargs)
|
||||
else:
|
||||
|
||||
@@ -1013,6 +1013,36 @@ class TelegramNotificationService:
|
||||
context=context,
|
||||
)
|
||||
|
||||
async def send_message_draft(
|
||||
self,
|
||||
message: str,
|
||||
chat_id: int,
|
||||
draft_id: int,
|
||||
context: Context | None = None,
|
||||
**kwargs: dict[str, Any],
|
||||
) -> None:
|
||||
"""Stream a partial message to a user while the message is being generated."""
|
||||
params = self._get_msg_kwargs(kwargs)
|
||||
|
||||
_LOGGER.debug(
|
||||
"Sending message draft %s in chat ID %s with params: %s",
|
||||
draft_id,
|
||||
chat_id,
|
||||
params,
|
||||
)
|
||||
|
||||
await self._send_msg(
|
||||
self.bot.send_message_draft,
|
||||
None,
|
||||
chat_id=chat_id,
|
||||
draft_id=draft_id,
|
||||
text=message,
|
||||
message_thread_id=params[ATTR_MESSAGE_THREAD_ID],
|
||||
parse_mode=params[ATTR_PARSER],
|
||||
read_timeout=params[ATTR_TIMEOUT],
|
||||
context=context,
|
||||
)
|
||||
|
||||
async def download_file(
|
||||
self,
|
||||
file_id: str,
|
||||
|
||||
@@ -31,6 +31,7 @@ DEFAULT_TRUSTED_NETWORKS = [ip_network("149.154.160.0/20"), ip_network("91.108.4
|
||||
|
||||
SERVICE_SEND_CHAT_ACTION = "send_chat_action"
|
||||
SERVICE_SEND_MESSAGE = "send_message"
|
||||
SERVICE_SEND_MESSAGE_DRAFT = "send_message_draft"
|
||||
SERVICE_SEND_PHOTO = "send_photo"
|
||||
SERVICE_SEND_MEDIA_GROUP = "send_media_group"
|
||||
SERVICE_SEND_STICKER = "send_sticker"
|
||||
@@ -90,6 +91,7 @@ ATTR_DATE = "date"
|
||||
ATTR_DISABLE_NOTIF = "disable_notification"
|
||||
ATTR_DISABLE_WEB_PREV = "disable_web_page_preview"
|
||||
ATTR_DIRECTORY_PATH = "directory_path"
|
||||
ATTR_DRAFT_ID = "draft_id"
|
||||
ATTR_EDITED_MSG = "edited_message"
|
||||
ATTR_FILE = "file"
|
||||
ATTR_FILE_ID = "file_id"
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
"send_message": {
|
||||
"service": "mdi:send"
|
||||
},
|
||||
"send_message_draft": {
|
||||
"service": "mdi:chat-processing"
|
||||
},
|
||||
"send_photo": {
|
||||
"service": "mdi:camera"
|
||||
},
|
||||
|
||||
@@ -1198,3 +1198,50 @@ download_file:
|
||||
example: "my_downloaded_file"
|
||||
selector:
|
||||
text:
|
||||
|
||||
send_message_draft:
|
||||
fields:
|
||||
entity_id:
|
||||
selector:
|
||||
entity:
|
||||
filter:
|
||||
domain: notify
|
||||
integration: telegram_bot
|
||||
multiple: true
|
||||
reorder: true
|
||||
message_thread_id:
|
||||
selector:
|
||||
number:
|
||||
mode: box
|
||||
draft_id:
|
||||
required: true
|
||||
selector:
|
||||
number:
|
||||
mode: box
|
||||
min: 1
|
||||
message:
|
||||
example: The garage door has been o
|
||||
required: true
|
||||
selector:
|
||||
text:
|
||||
parse_mode:
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- "html"
|
||||
- "markdown"
|
||||
- "markdownv2"
|
||||
- "plain_text"
|
||||
translation_key: "parse_mode"
|
||||
advanced:
|
||||
collapsed: true
|
||||
fields:
|
||||
config_entry_id:
|
||||
selector:
|
||||
config_entry:
|
||||
integration: telegram_bot
|
||||
chat_id:
|
||||
example: "[12345, 67890] or 12345"
|
||||
selector:
|
||||
text:
|
||||
multiple: true
|
||||
|
||||
@@ -951,6 +951,45 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"send_message_draft": {
|
||||
"description": "Stream a partial message to a user while the message is being generated.",
|
||||
"fields": {
|
||||
"chat_id": {
|
||||
"description": "One or more pre-authorized chat IDs to send the message draft to.",
|
||||
"name": "[%key:component::telegram_bot::services::edit_message::fields::chat_id::name%]"
|
||||
},
|
||||
"config_entry_id": {
|
||||
"description": "The config entry representing the Telegram bot to send the message draft.",
|
||||
"name": "[%key:component::telegram_bot::services::send_message::fields::config_entry_id::name%]"
|
||||
},
|
||||
"draft_id": {
|
||||
"description": "Unique identifier of the message draft. Changes of drafts with the same identifier are animated.",
|
||||
"name": "Draft ID"
|
||||
},
|
||||
"entity_id": {
|
||||
"description": "[%key:component::telegram_bot::services::send_message::fields::entity_id::description%]",
|
||||
"name": "[%key:component::telegram_bot::services::send_message::fields::entity_id::name%]"
|
||||
},
|
||||
"message": {
|
||||
"description": "Available part of the message for temporary notification.\nCan't parse entities? Format your message according to the [formatting options]({formatting_options_url}).",
|
||||
"name": "[%key:component::telegram_bot::services::send_message::fields::message::name%]"
|
||||
},
|
||||
"message_thread_id": {
|
||||
"description": "[%key:component::telegram_bot::services::send_message::fields::message_thread_id::description%]",
|
||||
"name": "[%key:component::telegram_bot::services::send_message::fields::message_thread_id::name%]"
|
||||
},
|
||||
"parse_mode": {
|
||||
"description": "[%key:component::telegram_bot::services::send_message::fields::parse_mode::description%]",
|
||||
"name": "[%key:component::telegram_bot::services::send_message::fields::parse_mode::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Send message draft",
|
||||
"sections": {
|
||||
"advanced": {
|
||||
"name": "[%key:component::telegram_bot::services::send_message::sections::advanced::name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"send_photo": {
|
||||
"description": "Sends a photo.",
|
||||
"fields": {
|
||||
|
||||
@@ -142,6 +142,7 @@ def mock_external_calls() -> Generator[None]:
|
||||
patch.object(BotMock, "get_me", return_value=test_user),
|
||||
patch.object(BotMock, "bot", test_user),
|
||||
patch.object(BotMock, "send_message", return_value=message),
|
||||
patch.object(BotMock, "send_message_draft", return_value=True),
|
||||
patch.object(BotMock, "send_photo", return_value=message),
|
||||
patch.object(BotMock, "send_media_group", side_effect=mock_send_media_group),
|
||||
patch.object(BotMock, "send_sticker", return_value=message),
|
||||
|
||||
@@ -1690,6 +1690,44 @@ async def test_set_message_reaction(
|
||||
)
|
||||
|
||||
|
||||
async def test_send_message_draft(
|
||||
hass: HomeAssistant,
|
||||
mock_broadcast_config_entry: MockConfigEntry,
|
||||
mock_external_calls: None,
|
||||
) -> None:
|
||||
"""Test send message draft."""
|
||||
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.send_message_draft",
|
||||
AsyncMock(return_value=True),
|
||||
) as mock:
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
"send_message_draft",
|
||||
{
|
||||
ATTR_CHAT_ID: 123456,
|
||||
ATTR_MESSAGE: "_Thinking..._",
|
||||
ATTR_MESSAGE_THREAD_ID: "123",
|
||||
ATTR_PARSER: PARSER_MD2,
|
||||
"draft_id": "3456",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
mock.assert_called_once_with(
|
||||
chat_id=123456,
|
||||
draft_id=3456,
|
||||
text="_Thinking..._",
|
||||
message_thread_id=123,
|
||||
parse_mode=PARSER_MD2,
|
||||
read_timeout=None,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "input"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user