diff --git a/homeassistant/components/telegram_bot/__init__.py b/homeassistant/components/telegram_bot/__init__.py index a39b6b300d2..b6ca7881615 100644 --- a/homeassistant/components/telegram_bot/__init__.py +++ b/homeassistant/components/telegram_bot/__init__.py @@ -553,7 +553,7 @@ class TelegramNotificationService: ) return params - def _send_msg(self, func_send, msg_error, *args_msg, **kwargs_msg): + def _send_msg(self, func_send, msg_error, message_tag, *args_msg, **kwargs_msg): """Send one message.""" try: @@ -572,7 +572,6 @@ class TelegramNotificationService: ATTR_CHAT_ID: chat_id, ATTR_MESSAGEID: message_id, } - message_tag = kwargs_msg.get(ATTR_MESSAGE_TAG) if message_tag is not None: event_data[ATTR_MESSAGE_TAG] = message_tag self.hass.bus.async_fire(EVENT_TELEGRAM_SENT, event_data) @@ -594,7 +593,17 @@ class TelegramNotificationService: for chat_id in self._get_target_chat_ids(target): _LOGGER.debug("Send message in chat ID %s with params: %s", chat_id, params) self._send_msg( - self.bot.sendMessage, "Error sending message", chat_id, text, **params + self.bot.send_message, + "Error sending message", + params[ATTR_MESSAGE_TAG], + chat_id, + text, + parse_mode=params[ATTR_PARSER], + disable_web_page_preview=params[ATTR_DISABLE_WEB_PREV], + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_to_message_id=params[ATTR_REPLY_TO_MSGID], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], ) def delete_message(self, chat_id=None, **kwargs): @@ -603,7 +612,7 @@ class TelegramNotificationService: message_id, _ = self._get_msg_ids(kwargs, chat_id) _LOGGER.debug("Delete message %s in chat ID %s", message_id, chat_id) deleted = self._send_msg( - self.bot.deleteMessage, "Error deleting message", chat_id, message_id + self.bot.delete_message, "Error deleting message", None, chat_id, message_id ) # reduce message_id anyway: if self._last_message_id[chat_id] is not None: @@ -628,26 +637,41 @@ class TelegramNotificationService: text = f"{title}\n{message}" if title else message _LOGGER.debug("Editing message with ID %s", message_id or inline_message_id) return self._send_msg( - self.bot.editMessageText, + self.bot.edit_message_text, "Error editing text message", + params[ATTR_MESSAGE_TAG], text, chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - **params, + parse_mode=params[ATTR_PARSER], + disable_web_page_preview=params[ATTR_DISABLE_WEB_PREV], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], ) if type_edit == SERVICE_EDIT_CAPTION: - func_send = self.bot.editMessageCaption - params[ATTR_CAPTION] = kwargs.get(ATTR_CAPTION) - else: - func_send = self.bot.editMessageReplyMarkup + return self._send_msg( + self.bot.edit_message_caption, + "Error editing message attributes", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + message_id=message_id, + inline_message_id=inline_message_id, + caption=kwargs.get(ATTR_CAPTION), + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + parse_mode=params[ATTR_PARSER], + ) + return self._send_msg( - func_send, + self.bot.edit_message_reply_markup, "Error editing message attributes", + params[ATTR_MESSAGE_TAG], chat_id=chat_id, message_id=message_id, inline_message_id=inline_message_id, - **params, + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], ) def answer_callback_query( @@ -662,26 +686,18 @@ class TelegramNotificationService: show_alert, ) self._send_msg( - self.bot.answerCallbackQuery, + self.bot.answer_callback_query, "Error sending answer callback query", + params[ATTR_MESSAGE_TAG], callback_query_id, text=message, show_alert=show_alert, - **params, + timeout=params[ATTR_TIMEOUT], ) def send_file(self, file_type=SERVICE_SEND_PHOTO, target=None, **kwargs): """Send a photo, sticker, video, or document.""" params = self._get_msg_kwargs(kwargs) - caption = kwargs.get(ATTR_CAPTION) - func_send = { - SERVICE_SEND_PHOTO: self.bot.sendPhoto, - SERVICE_SEND_STICKER: self.bot.sendSticker, - SERVICE_SEND_ANIMATION: self.bot.sendAnimation, - SERVICE_SEND_VIDEO: self.bot.sendVideo, - SERVICE_SEND_VOICE: self.bot.sendVoice, - SERVICE_SEND_DOCUMENT: self.bot.sendDocument, - }.get(file_type) file_content = load_data( self.hass, url=kwargs.get(ATTR_URL), @@ -691,17 +707,89 @@ class TelegramNotificationService: authentication=kwargs.get(ATTR_AUTHENTICATION), verify_ssl=kwargs.get(ATTR_VERIFY_SSL), ) + if file_content: for chat_id in self._get_target_chat_ids(target): - _LOGGER.debug("Send file to chat ID %s. Caption: %s", chat_id, caption) - self._send_msg( - func_send, - "Error sending file", - chat_id, - file_content, - caption=caption, - **params, - ) + _LOGGER.debug("Sending file to chat ID %s", chat_id) + + if file_type == SERVICE_SEND_PHOTO: + self._send_msg( + self.bot.send_photo, + "Error sending photo", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + photo=file_content, + caption=kwargs.get(ATTR_CAPTION), + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + parse_mode=params[ATTR_PARSER], + ) + + elif file_type == SERVICE_SEND_STICKER: + self._send_msg( + self.bot.send_sticker, + "Error sending sticker", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + sticker=file_content, + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + ) + + elif file_type == SERVICE_SEND_VIDEO: + self._send_msg( + self.bot.send_video, + "Error sending video", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + video=file_content, + caption=kwargs.get(ATTR_CAPTION), + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + parse_mode=params[ATTR_PARSER], + ) + elif file_type == SERVICE_SEND_DOCUMENT: + self._send_msg( + self.bot.send_document, + "Error sending document", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + document=file_content, + caption=kwargs.get(ATTR_CAPTION), + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + parse_mode=params[ATTR_PARSER], + ) + elif file_type == SERVICE_SEND_VOICE: + self._send_msg( + self.bot.send_voice, + "Error sending voice", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + voice=file_content, + caption=kwargs.get(ATTR_CAPTION), + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + ) + elif file_type == SERVICE_SEND_ANIMATION: + self._send_msg( + self.bot.send_animation, + "Error sending animation", + params[ATTR_MESSAGE_TAG], + chat_id=chat_id, + animation=file_content, + caption=kwargs.get(ATTR_CAPTION), + disable_notification=params[ATTR_DISABLE_NOTIF], + reply_markup=params[ATTR_REPLYMARKUP], + timeout=params[ATTR_TIMEOUT], + parse_mode=params[ATTR_PARSER], + ) + file_content.seek(0) else: _LOGGER.error("Can't send file with kwargs: %s", kwargs) @@ -716,19 +804,23 @@ class TelegramNotificationService: "Send location %s/%s to chat ID %s", latitude, longitude, chat_id ) self._send_msg( - self.bot.sendLocation, + self.bot.send_location, "Error sending location", + params[ATTR_MESSAGE_TAG], chat_id=chat_id, latitude=latitude, longitude=longitude, - **params, + disable_notification=params[ATTR_DISABLE_NOTIF], + timeout=params[ATTR_TIMEOUT], ) def leave_chat(self, chat_id=None): """Remove bot from chat.""" chat_id = self._get_target_chat_ids(chat_id)[0] _LOGGER.debug("Leave from chat ID %s", chat_id) - leaved = self._send_msg(self.bot.leaveChat, "Error leaving chat", chat_id) + leaved = self._send_msg( + self.bot.leave_chat, "Error leaving chat", None, chat_id + ) return leaved diff --git a/homeassistant/components/telegram_bot/manifest.json b/homeassistant/components/telegram_bot/manifest.json index 29f6ade8af4..80d9b50932e 100644 --- a/homeassistant/components/telegram_bot/manifest.json +++ b/homeassistant/components/telegram_bot/manifest.json @@ -2,7 +2,7 @@ "domain": "telegram_bot", "name": "Telegram bot", "documentation": "https://www.home-assistant.io/integrations/telegram_bot", - "requirements": ["python-telegram-bot==11.1.0", "PySocks==1.7.1"], + "requirements": ["python-telegram-bot==13.1", "PySocks==1.7.1"], "dependencies": ["http"], "codeowners": [] } diff --git a/homeassistant/components/telegram_bot/polling.py b/homeassistant/components/telegram_bot/polling.py index 8bdeef25118..b617826411d 100644 --- a/homeassistant/components/telegram_bot/polling.py +++ b/homeassistant/components/telegram_bot/polling.py @@ -3,10 +3,10 @@ import logging from telegram import Update from telegram.error import NetworkError, RetryAfter, TelegramError, TimedOut -from telegram.ext import Handler, Updater +from telegram.ext import CallbackContext, Dispatcher, Handler, Updater +from telegram.utils.types import HandlerArg from homeassistant.const import EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP -from homeassistant.core import callback from . import CONF_ALLOWED_CHAT_IDS, BaseTelegramBotEntity, initialize_bot @@ -18,12 +18,10 @@ async def async_setup_platform(hass, config): bot = initialize_bot(config) pol = TelegramPoll(bot, hass, config[CONF_ALLOWED_CHAT_IDS]) - @callback def _start_bot(_event): """Start the bot.""" pol.start_polling() - @callback def _stop_bot(_event): """Stop the bot.""" pol.stop_polling() @@ -34,15 +32,15 @@ async def async_setup_platform(hass, config): return True -def process_error(bot, update, error): +def process_error(update: Update, context: CallbackContext): """Telegram bot error handler.""" try: - raise error + raise context.error except (TimedOut, NetworkError, RetryAfter): # Long polling timeout or connection problem. Nothing serious. pass except TelegramError: - _LOGGER.error('Update "%s" caused error "%s"', update, error) + _LOGGER.error('Update "%s" caused error: "%s"', update, context.error) def message_handler(handler): @@ -59,10 +57,17 @@ def message_handler(handler): """Check is update valid.""" return isinstance(update, Update) - def handle_update(self, update, dispatcher): + def handle_update( + self, + update: HandlerArg, + dispatcher: Dispatcher, + check_result: object, + context: CallbackContext = None, + ): """Handle update.""" optional_args = self.collect_optional_args(dispatcher, update) - return self.callback(dispatcher.bot, update, **optional_args) + context.args = optional_args + return self.callback(update, context) return MessageHandler() @@ -89,6 +94,6 @@ class TelegramPoll(BaseTelegramBotEntity): """Stop the polling task.""" self.updater.stop() - def process_update(self, bot, update): + def process_update(self, update: HandlerArg, context: CallbackContext): """Process incoming message.""" self.process_message(update.to_dict()) diff --git a/homeassistant/components/telegram_bot/services.yaml b/homeassistant/components/telegram_bot/services.yaml index 0560e6541bd..5e2b06564dd 100644 --- a/homeassistant/components/telegram_bot/services.yaml +++ b/homeassistant/components/telegram_bot/services.yaml @@ -374,6 +374,9 @@ answer_callback_query: show_alert: description: Show a permanent notification. example: true + timeout: + description: Timeout for sending the answer. Will help with timeout errors (poor internet connection, etc) + example: "1000" delete_message: description: Delete a previously sent message. diff --git a/requirements_all.txt b/requirements_all.txt index ba1a755176a..3a048845626 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1816,7 +1816,7 @@ python-songpal==0.12 python-tado==0.8.1 # homeassistant.components.telegram_bot -python-telegram-bot==11.1.0 +python-telegram-bot==13.1 # homeassistant.components.vlc_telnet python-telnet-vlc==1.0.4