1 """Support for Telegram bots using webhooks."""
4 from http
import HTTPStatus
5 from ipaddress
import ip_address
10 from telegram
import Update
11 from telegram.error
import TimedOut
12 from telegram.ext
import Application, TypeHandler
18 from .
import CONF_TRUSTED_NETWORKS, CONF_URL, BaseTelegramBotEntity
20 _LOGGER = logging.getLogger(__name__)
22 TELEGRAM_WEBHOOK_URL =
"/api/telegram_webhooks"
23 REMOVE_WEBHOOK_URL =
""
24 SECRET_TOKEN_LENGTH = 32
28 """Set up the Telegram webhooks platform."""
31 alphabet = string.ascii_letters + string.digits +
"-_"
32 secret_token =
"".join(secrets.choice(alphabet)
for _
in range(SECRET_TOKEN_LENGTH))
34 pushbot =
PushBot(hass, bot, config, secret_token)
36 if not pushbot.webhook_url.startswith(
"https"):
37 _LOGGER.error(
"Invalid telegram webhook %s must be https", pushbot.webhook_url)
40 await pushbot.start_application()
41 webhook_registered = await pushbot.register_webhook()
42 if not webhook_registered:
45 hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, pushbot.stop_application)
46 hass.http.register_view(
51 config[CONF_TRUSTED_NETWORKS],
59 """Handles all the push/webhook logic and passes telegram updates to `self.handle_update`."""
61 def __init__(self, hass, bot, config, secret_token):
62 """Create Application before calling super()."""
68 self.
applicationapplication.add_handler(TypeHandler(Update, self.handle_update))
72 hass, require_ssl=
True, allow_internal=
False
74 self.
webhook_urlwebhook_url = f
"{self.base_url}{TELEGRAM_WEBHOOK_URL}"
77 _LOGGER.debug(
"Registering webhook URL: %s", self.
webhook_urlwebhook_url)
81 return await self.
botbot.set_webhook(
83 api_kwargs={
"secret_token": self.
secret_tokensecret_token},
88 _LOGGER.warning(
"Timeout trying to set webhook (retry #%d)", retry_num)
93 """Handle starting the Application object."""
98 """Query telegram and register the URL for our webhook."""
99 current_status = await self.
botbot.get_webhook_info()
101 last_error_date = getattr(current_status,
"last_error_date",
None)
102 if (last_error_date
is not None)
and (isinstance(last_error_date, int)):
103 last_error_date = dt.datetime.fromtimestamp(last_error_date)
105 "Telegram webhook last_error_date: %s. Status: %s",
110 _LOGGER.debug(
"telegram webhook status: %s", current_status)
112 if current_status
and current_status[
"url"] != self.
webhook_urlwebhook_url:
115 _LOGGER.debug(
"Set new telegram webhook %s", self.
webhook_urlwebhook_url)
117 _LOGGER.error(
"Set telegram webhook failed %s", self.
webhook_urlwebhook_url)
123 """Handle gracefully stopping the Application object."""
129 """Query telegram and deregister the URL for our webhook."""
130 _LOGGER.debug(
"Deregistering webhook URL")
131 await self.
botbot.delete_webhook()
135 """View for handling webhook calls from Telegram."""
137 requires_auth =
False
138 url = TELEGRAM_WEBHOOK_URL
139 name =
"telegram_webhooks"
141 def __init__(self, hass, bot, application, trusted_networks, secret_token):
142 """Initialize by storing stuff needed for setting up our webhook endpoint."""
150 """Accept the POST from telegram."""
151 real_ip = ip_address(request.remote)
152 if not any(real_ip
in net
for net
in self.
trusted_networkstrusted_networks):
153 _LOGGER.warning(
"Access denied from %s", real_ip)
154 return self.json_message(
"Access denied", HTTPStatus.UNAUTHORIZED)
155 secret_token_header = request.headers.get(
"X-Telegram-Bot-Api-Secret-Token")
156 if secret_token_header
is None or self.
secret_tokensecret_token != secret_token_header:
157 _LOGGER.warning(
"Invalid secret token from %s", real_ip)
158 return self.json_message(
"Access denied", HTTPStatus.UNAUTHORIZED)
161 update_data = await request.json()
163 return self.json_message(
"Invalid JSON", HTTPStatus.BAD_REQUEST)
165 update = Update.de_json(update_data, self.
botbot)
166 _LOGGER.debug(
"Received Update on %s: %s", self.
urlurl, update)
167 await self.
applicationapplication.process_update(update)
def __init__(self, hass, bot, application, trusted_networks, secret_token)
def register_webhook(self)
def stop_application(self, event=None)
def _try_to_set_webhook(self)
def start_application(self)
def deregister_webhook(self)
def __init__(self, hass, bot, config, secret_token)
def async_setup_platform(hass, bot, config)
str get_url(HomeAssistant hass, *bool require_current_request=False, bool require_ssl=False, bool require_standard_port=False, bool require_cloud=False, bool allow_internal=True, bool allow_external=True, bool allow_cloud=True, bool|None allow_ip=None, bool|None prefer_external=None, bool prefer_cloud=False)