Skip to content

Commit

Permalink
Handle telegram polling errors (home-assistant#124327)
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinHjelmare authored and bramkragten committed Sep 3, 2024
1 parent 009989d commit 9a690ed
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 5 deletions.
16 changes: 12 additions & 4 deletions homeassistant/components/telegram_bot/polling.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,22 @@ async def async_setup_platform(hass, bot, config):

async def process_error(update: Update, context: CallbackContext) -> None:
"""Telegram bot error handler."""
if context.error:
error_callback(context.error, update)


def error_callback(error: Exception, update: Update | None = None) -> None:
"""Log the error."""
try:
if context.error:
raise context.error
raise error
except (TimedOut, NetworkError, RetryAfter):
# Long polling timeout or connection problem. Nothing serious.
pass
except TelegramError:
_LOGGER.error('Update "%s" caused error: "%s"', update, context.error)
if update is not None:
_LOGGER.error('Update "%s" caused error: "%s"', update, error)
else:
_LOGGER.error("%s: %s", error.__class__.__name__, error)


class PollBot(BaseTelegramBotEntity):
Expand All @@ -53,7 +61,7 @@ async def start_polling(self, event=None):
"""Start the polling task."""
_LOGGER.debug("Starting polling")
await self.application.initialize()
await self.application.updater.start_polling()
await self.application.updater.start_polling(error_callback=error_callback)
await self.application.start()

async def stop_polling(self, event=None):
Expand Down
103 changes: 102 additions & 1 deletion tests/components/telegram_bot/test_telegram_bot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
"""Tests for the telegram_bot component."""

from unittest.mock import AsyncMock, patch
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from telegram import Update
from telegram.error import NetworkError, RetryAfter, TelegramError, TimedOut

from homeassistant.components.telegram_bot import (
ATTR_MESSAGE,
Expand All @@ -11,6 +14,7 @@
SERVICE_SEND_MESSAGE,
)
from homeassistant.components.telegram_bot.webhooks import TELEGRAM_WEBHOOK_URL
from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.core import Context, HomeAssistant
from homeassistant.setup import async_setup_component

Expand Down Expand Up @@ -188,6 +192,103 @@ async def test_polling_platform_message_text_update(
assert isinstance(events[0].context, Context)


@pytest.mark.parametrize(
("error", "log_message"),
[
(
TelegramError("Telegram error"),
'caused error: "Telegram error"',
),
(NetworkError("Network error"), ""),
(RetryAfter(42), ""),
(TimedOut("TimedOut error"), ""),
],
)
async def test_polling_platform_add_error_handler(
hass: HomeAssistant,
config_polling: dict[str, Any],
update_message_text: dict[str, Any],
caplog: pytest.LogCaptureFixture,
error: Exception,
log_message: str,
) -> None:
"""Test polling add error handler."""
with patch(
"homeassistant.components.telegram_bot.polling.ApplicationBuilder"
) as application_builder_class:
await async_setup_component(
hass,
DOMAIN,
config_polling,
)
await hass.async_block_till_done()

application = (
application_builder_class.return_value.bot.return_value.build.return_value
)
application.updater.stop = AsyncMock()
application.stop = AsyncMock()
application.shutdown = AsyncMock()
process_error = application.add_error_handler.call_args[0][0]
application.bot.defaults.tzinfo = None
update = Update.de_json(update_message_text, application.bot)

await process_error(update, MagicMock(error=error))

assert log_message in caplog.text


@pytest.mark.parametrize(
("error", "log_message"),
[
(
TelegramError("Telegram error"),
"TelegramError: Telegram error",
),
(NetworkError("Network error"), ""),
(RetryAfter(42), ""),
(TimedOut("TimedOut error"), ""),
],
)
async def test_polling_platform_start_polling_error_callback(
hass: HomeAssistant,
config_polling: dict[str, Any],
caplog: pytest.LogCaptureFixture,
error: Exception,
log_message: str,
) -> None:
"""Test polling add error handler."""
with patch(
"homeassistant.components.telegram_bot.polling.ApplicationBuilder"
) as application_builder_class:
await async_setup_component(
hass,
DOMAIN,
config_polling,
)
await hass.async_block_till_done()

application = (
application_builder_class.return_value.bot.return_value.build.return_value
)
application.initialize = AsyncMock()
application.updater.start_polling = AsyncMock()
application.start = AsyncMock()
application.updater.stop = AsyncMock()
application.stop = AsyncMock()
application.shutdown = AsyncMock()

hass.bus.async_fire(EVENT_HOMEASSISTANT_START)
await hass.async_block_till_done()
error_callback = application.updater.start_polling.call_args.kwargs[
"error_callback"
]

error_callback(error)

assert log_message in caplog.text


async def test_webhook_endpoint_unauthorized_update_doesnt_generate_telegram_text_event(
hass: HomeAssistant,
webhook_platform,
Expand Down

0 comments on commit 9a690ed

Please sign in to comment.