Ejemplo n.º 1
0
        def stop_on_open(**payload):
            rtm_client = payload["rtm_client"]

            if rtm_client._connection_attempts == 1:
                raise e.SlackApiError("Test Error", {"headers": {"Retry-After": 0.001}})
            else:
                self.assertEqual(rtm_client._connection_attempts, 2)
                rtm_client.stop()
Ejemplo n.º 2
0
    async def _retrieve_websocket_info(self):
        """Retrieves the WebSocket info from Slack.

        Returns:
            A tuple of websocket information.
            e.g.
            (
                "wss://...",
                {
                    "self": {"id": "U01234ABC","name": "robotoverlord"},
                    "team": {
                        "domain": "exampledomain",
                        "id": "T123450FP",
                        "name": "ExampleName"
                    }
                }
            )

        Raises:
            SlackApiError: Unable to retrieve RTM URL from Slack.
        """
        if self._web_client is None:
            self._web_client = WebClient(
                token=self.token,
                base_url=self.base_url,
                timeout=self.timeout,
                ssl=self.ssl,
                proxy=self.proxy,
                run_async=True,
                loop=self._event_loop,
                session=self._session,
                headers=self.headers,
            )
        self._logger.debug("Retrieving websocket info.")
        use_rtm_start = self.connect_method in ["rtm.start", "rtm_start"]
        if self.run_async:
            if use_rtm_start:
                resp = await self._web_client.rtm_start()
            else:
                resp = await self._web_client.rtm_connect()
        else:
            if use_rtm_start:
                resp = self._web_client.rtm_start()
            else:
                resp = self._web_client.rtm_connect()

        url = resp.get("url")
        if url is None:
            msg = "Unable to retrieve RTM URL from Slack."
            raise client_err.SlackApiError(message=msg, response=resp)
        return url, resp.data
Ejemplo n.º 3
0
    def validate(self):
        """Check if the response from Slack was successful.

        Returns:
            (AsyncSlackResponse)
                This method returns it's own object. e.g. 'self'

        Raises:
            SlackApiError: The request to the Slack API failed.
        """
        if self.status_code == 200 and self.data and self.data.get(
                "ok", False):
            return self
        msg = "The request to the Slack API failed."
        raise e.SlackApiError(message=msg, response=self)
Ejemplo n.º 4
0
    def validate(self):
        """Check if the response from Slack was successful.

        Returns:
            (SlackResponse)
                This method returns it's own object. e.g. 'self'

        Raises:
            SlackApiError: The request to the Slack API failed.
        """
        if self.status_code == 200 and self.data.get("ok", False):
            self._logger.debug("Received the following response: %s",
                               self.data)
            return self
        msg = "The request to the Slack API failed."
        raise e.SlackApiError(message=msg, response=self)
Ejemplo n.º 5
0
    def validate(self):
        """Check if the response from Slack was successful.

        Returns:
            (SlackResponse)
                This method returns it's own object. e.g. 'self'

        Raises:
            SlackApiError: The request to the Slack API failed.
        """
        if self._logger.level <= logging.DEBUG:
            self._logger.debug("Received the following response - "
                               f"status: {self.status_code}, "
                               f"headers: {dict(self.headers)}, "
                               f"body: {self.data}")
        if self.status_code == 200 and self.data and self.data.get(
                "ok", False):
            return self
        msg = "The request to the Slack API failed."
        raise e.SlackApiError(message=msg, response=self)
Ejemplo n.º 6
0
    async def _request(self, *, http_verb, api_url, req_args):
        """Submit the HTTP request with the running session or a new session.
        Returns:
            A dictionary of the response data.
        """
        session = None
        use_running_session = self.session and not self.session.closed
        if use_running_session:
            session = self.session
        else:
            session = aiohttp.ClientSession(
                timeout=aiohttp.ClientTimeout(total=self.timeout),
                auth=req_args.pop("auth", None),
            )

        response = None
        try:
            async with session.request(http_verb, api_url, **req_args) as res:
                data = {}
                try:
                    data = await res.json()
                except aiohttp.ContentTypeError:
                    self._logger.debug(
                        f"No response data returned from the following API call: {api_url}."
                    )
                except json.decoder.JSONDecodeError as e:
                    message = f"Failed to parse the response body: {str(e)}"
                    raise err.SlackApiError(message, res)

                response = {
                    "data": data,
                    "headers": res.headers,
                    "status_code": res.status,
                }
        finally:
            if not use_running_session:
                await session.close()
        return response
Ejemplo n.º 7
0
    def _urllib_api_call(
        self,
        *,
        token: str = None,
        url: str,
        query_params: Dict[str, str] = {},
        json_body: Dict = {},
        body_params: Dict[str, str] = {},
        files: Dict[str, io.BytesIO] = {},
        additional_headers: Dict[str, str] = {},
    ) -> SlackResponse:
        """Performs a Slack API request and returns the result.

        :param token: Slack API Token (either bot token or user token)
        :param url: a complete URL (e.g., https://www.slack.com/api/chat.postMessage)
        :param query_params: query string
        :param json_body: json data structure (it's still a dict at this point),
            if you give this argument, body_params and files will be skipped
        :param body_params: form params
        :param files: files to upload
        :param additional_headers: request headers to append
        :return: API response
        """

        files_to_close: List[BinaryIO] = []
        try:
            # True/False -> "1"/"0"
            query_params = convert_bool_to_0_or_1(query_params)
            body_params = convert_bool_to_0_or_1(body_params)

            if self._logger.level <= logging.DEBUG:

                def convert_params(values: dict) -> dict:
                    if not values or not isinstance(values, dict):
                        return {}
                    return {
                        k: ("(bytes)" if isinstance(v, bytes) else v)
                        for k, v in values.items()
                    }

                headers = {
                    k: "(redacted)" if k.lower() == "authorization" else v
                    for k, v in additional_headers.items()
                }
                self._logger.debug(
                    f"Sending a request - url: {url}, "
                    f"query_params: {convert_params(query_params)}, "
                    f"body_params: {convert_params(body_params)}, "
                    f"files: {convert_params(files)}, "
                    f"json_body: {json_body}, "
                    f"headers: {headers}")

            request_data = {}
            if files is not None and isinstance(files,
                                                dict) and len(files) > 0:
                if body_params:
                    for k, v in body_params.items():
                        request_data.update({k: v})

                for k, v in files.items():
                    if isinstance(v, str):
                        f: BinaryIO = open(v.encode("utf-8", "ignore"), "rb")
                        files_to_close.append(f)
                        request_data.update({k: f})
                    elif isinstance(v, (bytearray, bytes)):
                        request_data.update({k: io.BytesIO(v)})
                    else:
                        request_data.update({k: v})

            request_headers = self._build_urllib_request_headers(
                token=token or self.token,
                has_json=json is not None,
                has_files=files is not None,
                additional_headers=additional_headers,
            )
            request_args = {
                "headers": request_headers,
                "data": request_data,
                "params": body_params,
                "files": files,
                "json": json_body,
            }
            if query_params:
                q = urlencode(query_params)
                url = f"{url}&{q}" if "?" in url else f"{url}?{q}"

            response = self._perform_urllib_http_request(url=url,
                                                         args=request_args)
            if response.get("body"):
                try:
                    response_body_data: dict = json.loads(response["body"])
                except json.decoder.JSONDecodeError as e:
                    message = f"Failed to parse the response body: {str(e)}"
                    raise err.SlackApiError(message, response)
            else:
                response_body_data: dict = None

            if query_params:
                all_params = copy.copy(body_params)
                all_params.update(query_params)
            else:
                all_params = body_params
            request_args["params"] = all_params  # for backward-compatibility

            return SlackResponse(
                client=self,
                http_verb="POST",  # you can use POST method for all the Web APIs
                api_url=url,
                req_args=request_args,
                data=response_body_data,
                headers=dict(response["headers"]),
                status_code=response["status"],
                use_sync_aiohttp=False,
            ).validate()
        finally:
            for f in files_to_close:
                if not f.closed:
                    f.close()