Repository URL to install this package:
|
Version:
3.7.0 ▾
|
import json
import logging
import urllib
from http.client import HTTPResponse
from ssl import SSLContext
from typing import Dict, Union, List, Optional
from urllib.error import HTTPError
from urllib.request import Request, urlopen, OpenerDirector, ProxyHandler, HTTPSHandler
from slack.errors import SlackRequestError
from .internal_utils import _build_body, _build_request_headers, _debug_log_response
from .webhook_response import WebhookResponse
from ..web.classes.attachments import Attachment
from ..web.classes.blocks import Block
class WebhookClient:
logger = logging.getLogger(__name__)
def __init__(
self,
url: str,
timeout: int = 30,
ssl: Optional[SSLContext] = None,
proxy: Optional[str] = None,
default_headers: Optional[Dict[str, str]] = None,
):
self.url = url
self.timeout = timeout
self.ssl = ssl
self.proxy = proxy
self.default_headers = default_headers if default_headers else {}
def send(
self,
*,
text: Optional[str] = None,
attachments: Optional[List[Union[Dict[str, any], Attachment]]] = None,
blocks: Optional[List[Union[Dict[str, any], Block]]] = None,
response_type: Optional[str] = None,
headers: Optional[Dict[str, str]] = None,
) -> WebhookResponse:
return self.send_dict(
body={
"text": text,
"attachments": attachments,
"blocks": blocks,
"response_type": response_type,
},
headers=headers,
)
def send_dict(
self, body: Dict[str, any], headers: Optional[Dict[str, str]] = None
) -> WebhookResponse:
return self._perform_http_request(
body=_build_body(body),
headers=_build_request_headers(self.default_headers, headers),
)
def _perform_http_request(
self, *, body: Dict[str, any], headers: Dict[str, str]
) -> WebhookResponse:
body = json.dumps(body)
headers["Content-Type"] = "application/json;charset=utf-8"
if self.logger.level <= logging.DEBUG:
self.logger.debug(
f"Sending a request - url: {self.url}, body: {body}, headers: {headers}"
)
try:
url = self.url
opener: Optional[OpenerDirector] = None
# for security (BAN-B310)
if url.lower().startswith("http"):
req = Request(
method="POST", url=url, data=body.encode("utf-8"), headers=headers
)
if self.proxy is not None:
if isinstance(self.proxy, str):
opener = urllib.request.build_opener(
ProxyHandler({"http": self.proxy, "https": self.proxy}),
HTTPSHandler(context=self.ssl),
)
else:
raise SlackRequestError(
f"Invalid proxy detected: {self.proxy} must be a str value"
)
else:
raise SlackRequestError(f"Invalid URL detected: {url}")
# NOTE: BAN-B310 is already checked above
resp: Optional[HTTPResponse] = None
if opener:
resp = opener.open(req, timeout=self.timeout) # skipcq: BAN-B310
else:
resp = urlopen( # skipcq: BAN-B310
req, context=self.ssl, timeout=self.timeout
)
charset: str = resp.headers.get_content_charset() or "utf-8"
response_body: str = resp.read().decode(charset)
resp = WebhookResponse(
url=url,
status_code=resp.status,
body=response_body,
headers=resp.headers,
)
_debug_log_response(self.logger, resp)
return resp
except HTTPError as e:
charset = e.headers.get_content_charset() or "utf-8"
body: str = e.read().decode(charset) # read the response body here
resp = WebhookResponse(
url=url,
status_code=e.code,
body=body,
headers=e.headers,
)
if e.code == 429:
# for backward-compatibility with WebClient (v.2.5.0 or older)
resp.headers["Retry-After"] = resp.headers["retry-after"]
_debug_log_response(self.logger, resp)
return resp
except Exception as err:
self.logger.error(f"Failed to send a request to Slack API server: {err}")
raise err