Cleanup error handling for Telegram bot (#163689)

This commit is contained in:
hanwg
2026-02-21 16:19:06 +08:00
committed by GitHub
parent c3b0f7ba55
commit 047d5735d8
3 changed files with 36 additions and 77 deletions
@@ -468,7 +468,7 @@ async def _async_send_telegram_message(service: ServiceCall) -> ServiceResponse:
targets = _build_targets(service)
service_responses: JsonValueType = []
errors: list[tuple[HomeAssistantError, str]] = []
errors: list[tuple[Exception, str]] = []
# invoke the service for each target
for target_config_entry, target_chat_id, target_notify_entity_id in targets:
@@ -495,7 +495,7 @@ async def _async_send_telegram_message(service: ServiceCall) -> ServiceResponse:
assert isinstance(service_responses, list)
service_responses.extend(formatted_responses)
except HomeAssistantError as ex:
except (HomeAssistantError, TelegramError) as ex:
target = target_notify_entity_id or str(target_chat_id)
errors.append((ex, target))
+26 -69
View File
@@ -433,7 +433,6 @@ class TelegramNotificationService:
async def _send_msgs(
self,
func_send: Callable,
msg_error: str,
message_tag: str | None,
*args_msg: Any,
context: Context | None = None,
@@ -459,12 +458,10 @@ class TelegramNotificationService:
response: Message = await self._send_msg(
func_send,
msg_error,
message_tag,
chat_id,
*args_msg,
context=context,
suppress_error=len(chat_ids) > 1,
**kwargs_msg,
)
if response:
@@ -475,58 +472,39 @@ class TelegramNotificationService:
async def _send_msg(
self,
func_send: Callable,
msg_error: str,
message_tag: str | None,
*args_msg: Any,
context: Context | None = None,
suppress_error: bool = False,
**kwargs_msg: Any,
) -> Any:
"""Send one message."""
try:
out = await func_send(*args_msg, **kwargs_msg)
if isinstance(out, Message):
chat_id = out.chat_id
message_id = out.message_id
self._last_message_id[chat_id] = message_id
_LOGGER.debug(
"Last message ID: %s (from chat_id %s)",
self._last_message_id,
chat_id,
)
event_data: dict[str, Any] = {
ATTR_CHAT_ID: chat_id,
ATTR_MESSAGEID: message_id,
}
if message_tag is not None:
event_data[ATTR_MESSAGE_TAG] = message_tag
if kwargs_msg.get(ATTR_MESSAGE_THREAD_ID) is not None:
event_data[ATTR_MESSAGE_THREAD_ID] = kwargs_msg[
ATTR_MESSAGE_THREAD_ID
]
event_data["bot"] = _get_bot_info(self.bot, self.config)
self.hass.bus.async_fire(
EVENT_TELEGRAM_SENT, event_data, context=context
)
async_dispatcher_send(
self.hass, signal(self.bot), EVENT_TELEGRAM_SENT, event_data
)
except TelegramError as exc:
if not suppress_error:
raise HomeAssistantError(
translation_domain=DOMAIN,
translation_key="action_failed",
translation_placeholders={"error": str(exc)},
) from exc
_LOGGER.error(
"%s: %s. Args: %s, kwargs: %s", msg_error, exc, args_msg, kwargs_msg
out = await func_send(*args_msg, **kwargs_msg)
if isinstance(out, Message):
chat_id = out.chat_id
message_id = out.message_id
self._last_message_id[chat_id] = message_id
_LOGGER.debug(
"Last message ID: %s (from chat_id %s)",
self._last_message_id,
chat_id,
)
event_data: dict[str, Any] = {
ATTR_CHAT_ID: chat_id,
ATTR_MESSAGEID: message_id,
}
if message_tag is not None:
event_data[ATTR_MESSAGE_TAG] = message_tag
if kwargs_msg.get(ATTR_MESSAGE_THREAD_ID) is not None:
event_data[ATTR_MESSAGE_THREAD_ID] = kwargs_msg[ATTR_MESSAGE_THREAD_ID]
event_data["bot"] = _get_bot_info(self.bot, self.config)
self.hass.bus.async_fire(EVENT_TELEGRAM_SENT, event_data, context=context)
async_dispatcher_send(
self.hass, signal(self.bot), EVENT_TELEGRAM_SENT, event_data
)
return None
return out
async def send_message(
@@ -542,7 +520,6 @@ class TelegramNotificationService:
params = self._get_msg_kwargs(kwargs)
return await self._send_msgs(
self.bot.send_message,
"Error sending message",
params[ATTR_MESSAGE_TAG],
text,
chat_id=chat_id,
@@ -567,7 +544,6 @@ class TelegramNotificationService:
_LOGGER.debug("Delete message %s in chat ID %s", message_id, chat_id)
deleted: bool = await self._send_msg(
self.bot.delete_message,
"Error deleting message",
None,
chat_id,
message_id,
@@ -644,7 +620,6 @@ class TelegramNotificationService:
return await self._send_msg(
self.bot.edit_message_media,
"Error editing message media",
params[ATTR_MESSAGE_TAG],
media=media,
chat_id=chat_id,
@@ -678,7 +653,6 @@ class TelegramNotificationService:
_LOGGER.debug("Editing message with ID %s", message_id or inline_message_id)
return await self._send_msg(
self.bot.edit_message_text,
"Error editing text message",
params[ATTR_MESSAGE_TAG],
text,
chat_id=chat_id,
@@ -693,7 +667,6 @@ class TelegramNotificationService:
if type_edit == SERVICE_EDIT_CAPTION:
return await self._send_msg(
self.bot.edit_message_caption,
"Error editing message attributes",
params[ATTR_MESSAGE_TAG],
chat_id=chat_id,
message_id=message_id,
@@ -707,7 +680,6 @@ class TelegramNotificationService:
return await self._send_msg(
self.bot.edit_message_reply_markup,
"Error editing message attributes",
params[ATTR_MESSAGE_TAG],
chat_id=chat_id,
message_id=message_id,
@@ -735,7 +707,6 @@ class TelegramNotificationService:
)
await self._send_msg(
self.bot.answer_callback_query,
"Error sending answer callback query",
params[ATTR_MESSAGE_TAG],
callback_query_id,
text=message,
@@ -756,7 +727,6 @@ class TelegramNotificationService:
_LOGGER.debug("Send action %s in chat ID %s", chat_action, chat_id)
is_successful = await self._send_msg(
self.bot.send_chat_action,
"Error sending action",
None,
chat_id=chat_id,
action=chat_action,
@@ -791,7 +761,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_PHOTO:
return await self._send_msgs(
self.bot.send_photo,
"Error sending photo",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
photo=file_content,
@@ -808,7 +777,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_STICKER:
return await self._send_msgs(
self.bot.send_sticker,
"Error sending sticker",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
sticker=file_content,
@@ -823,7 +791,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_VIDEO:
return await self._send_msgs(
self.bot.send_video,
"Error sending video",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
video=file_content,
@@ -840,7 +807,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_DOCUMENT:
return await self._send_msgs(
self.bot.send_document,
"Error sending document",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
document=file_content,
@@ -857,7 +823,6 @@ class TelegramNotificationService:
if file_type == SERVICE_SEND_VOICE:
return await self._send_msgs(
self.bot.send_voice,
"Error sending voice",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
voice=file_content,
@@ -873,7 +838,6 @@ class TelegramNotificationService:
# SERVICE_SEND_ANIMATION
return await self._send_msgs(
self.bot.send_animation,
"Error sending animation",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
animation=file_content,
@@ -899,7 +863,6 @@ class TelegramNotificationService:
if stickerid:
return await self._send_msgs(
self.bot.send_sticker,
"Error sending sticker",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
sticker=stickerid,
@@ -925,7 +888,6 @@ class TelegramNotificationService:
params = self._get_msg_kwargs(kwargs)
return await self._send_msgs(
self.bot.send_location,
"Error sending location",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
latitude=latitude,
@@ -951,7 +913,6 @@ class TelegramNotificationService:
openperiod = kwargs.get(ATTR_OPEN_PERIOD)
return await self._send_msgs(
self.bot.send_poll,
"Error sending poll",
params[ATTR_MESSAGE_TAG],
chat_id=kwargs[ATTR_CHAT_ID],
question=question,
@@ -974,9 +935,7 @@ class TelegramNotificationService:
) -> Any:
"""Remove bot from chat."""
_LOGGER.debug("Leave from chat ID %s", chat_id)
return await self._send_msg(
self.bot.leave_chat, "Error leaving chat", None, chat_id, context=context
)
return await self._send_msg(self.bot.leave_chat, None, chat_id, context=context)
async def set_message_reaction(
self,
@@ -1000,7 +959,6 @@ class TelegramNotificationService:
await self._send_msg(
self.bot.set_message_reaction,
"Error setting message reaction",
params[ATTR_MESSAGE_TAG],
chat_id,
message_id,
@@ -1023,7 +981,6 @@ class TelegramNotificationService:
directory_path = self.hass.config.path(DOMAIN)
file: File = await self._send_msg(
self.bot.get_file,
"Error getting file",
None,
file_id=file_id,
context=context,
@@ -353,7 +353,7 @@ async def test_send_sticker_partial_error(
assert mock_send_sticker.call_count == 2
assert err.value.translation_key == "multiple_errors"
assert err.value.translation_placeholders == {
"errors": "`entity_id` notify.mock_title_mock_chat_1: Action failed. mock network error\n`entity_id` notify.mock_title_mock_chat_2: Action failed. mock network error"
"errors": "`entity_id` notify.mock_title_mock_chat_1: mock network error\n`entity_id` notify.mock_title_mock_chat_2: mock network error"
}
@@ -364,7 +364,7 @@ async def test_send_sticker_error(hass: HomeAssistant, webhook_bot) -> None:
) as mock_bot:
mock_bot.side_effect = NetworkError("mock network error")
with pytest.raises(HomeAssistantError) as err:
with pytest.raises(TelegramError) as err:
await hass.services.async_call(
DOMAIN,
SERVICE_SEND_STICKER,
@@ -377,8 +377,8 @@ async def test_send_sticker_error(hass: HomeAssistant, webhook_bot) -> None:
await hass.async_block_till_done()
mock_bot.assert_called_once()
assert err.value.translation_domain == DOMAIN
assert err.value.translation_key == "action_failed"
assert err.typename == "NetworkError"
assert err.value.message == "mock network error"
async def test_send_message_with_invalid_inline_keyboard(
@@ -2264,7 +2264,7 @@ async def test_download_file_when_bot_failed_to_get_file(
"homeassistant.components.telegram_bot.bot.Bot.get_file",
AsyncMock(side_effect=TelegramError("failed to get file")),
),
pytest.raises(HomeAssistantError) as err,
pytest.raises(TelegramError) as err,
):
await hass.services.async_call(
DOMAIN,
@@ -2273,7 +2273,9 @@ async def test_download_file_when_bot_failed_to_get_file(
blocking=True,
)
await hass.async_block_till_done()
assert err.value.translation_key == "action_failed"
assert err.typename == "TelegramError"
assert err.value.message == "failed to get file"
async def test_download_file_when_empty_file_path(