async def _dispatch_event(self, event, data=None): """Dispatches the event and executes any associated callbacks. Note: To prevent the app from crashing due to callback errors. We catch all exceptions and send all data to the logger. Args: event (str): The type of event. e.g. 'bot_added' data (dict): The data Slack sent. e.g. { "type": "bot_added", "bot": { "id": "B024BE7LH", "app_id": "A4H1JB4AZ", "name": "hugbot" } } """ if self._logger.level <= logging.DEBUG: self._logger.debug("Received an event: '%s' - %s", event, data) for callback in self._callbacks[event]: self._logger.debug( "Running %s callbacks for event: '%s'", len(self._callbacks[event]), event, ) try: if self._stopped and event not in ["close", "error"]: # Don't run callbacks if client was stopped unless they're # close/error callbacks. break if inspect.iscoroutinefunction(callback): await callback(rtm_client=self, web_client=self._web_client, data=data) else: if self.run_async is True: raise client_err.SlackRequestError( f'The callback "{callback.__name__}" is NOT a coroutine. ' "Running such with run_async=True is unsupported. " "Consider adding async/await to the method " "or going with run_async=False if your app is not really non-blocking." ) payload = { "rtm_client": self, "web_client": self._web_client, "data": data, } callback(**payload) except Exception as err: name = callback.__name__ module = callback.__module__ msg = f"When calling '#{name}()' in the '{module}' module the following error was raised: {err}" self._logger.error(msg) raise
def api_call( self, api_method: str, *, http_verb: str = "POST", files: dict = None, data: Union[dict, FormData] = None, params: dict = None, json: dict = None, headers: dict = None, auth: dict = None, ) -> Union[asyncio.Future, SlackResponse]: """Create a request and execute the API call to Slack. Args: api_method (str): The target Slack API method. e.g. 'chat.postMessage' http_verb (str): HTTP Verb. e.g. 'POST' files (dict): Files to multipart upload. e.g. {imageORfile: file_objectORfile_path} data: The body to attach to the request. If a dictionary is provided, form-encoding will take place. e.g. {'key1': 'value1', 'key2': 'value2'} params (dict): The URL parameters to append to the URL. e.g. {'key1': 'value1', 'key2': 'value2'} json (dict): JSON for the body to attach to the request (if files or data is not specified). e.g. {'key1': 'value1', 'key2': 'value2'} Returns: (SlackResponse) The server's response to an HTTP request. Data from the response can be accessed like a dict. If the response included 'next_cursor' it can be iterated on to execute subsequent requests. Raises: SlackApiError: The following Slack API call failed: 'chat.postMessage'. SlackRequestError: Json data can only be submitted as POST requests. """ has_json = json is not None has_files = files is not None if has_json and http_verb != "POST": msg = "Json data can only be submitted as POST requests. GET requests should use the 'params' argument." raise err.SlackRequestError(msg) api_url = self._get_url(api_method) if auth: auth = BasicAuth(auth["client_id"], auth["client_secret"]) if data: data = {k: v for k, v in data.items() if v is not None} if files: files = {k: v for k, v in files.items() if v is not None} if params: params = {k: v for k, v in params.items() if v is not None} req_args = { "headers": self._get_headers(has_json, has_files, headers), "data": data, "files": files, "params": params, "json": json, "ssl": self.ssl, "proxy": self.proxy, "auth": auth, } if self._event_loop is None: self._event_loop = self._get_event_loop() show_2020_01_deprecation(api_method) future = asyncio.ensure_future( self._send(http_verb=http_verb, api_url=api_url, req_args=req_args), loop=self._event_loop, ) if self.run_async: return future return self._event_loop.run_until_complete(future)
def api_call( self, api_method: str, *, http_verb: str = "POST", files: dict = None, data: dict = None, params: dict = None, json: dict = None, ): """Create a request and execute the API call to Slack. Args: api_method (str): The target Slack API method. e.g. 'chat.postMessage' http_verb (str): HTTP Verb. e.g. 'POST' files (dict): Files to multipart upload. e.g. {imageORfile: file_objectORfile_path} data: The body to attach to the request. If a dictionary is provided, form-encoding will take place. e.g. {'key1': 'value1', 'key2': 'value2'} params (dict): The URL parameters to append to the URL. e.g. {'key1': 'value1', 'key2': 'value2'} json (dict): JSON for the body to attach to the request (if files or data is not specified). e.g. {'key1': 'value1', 'key2': 'value2'} Returns: (SlackResponse) The server's response to an HTTP request. Data from the response can be accessed like a dict. If the response included 'next_cursor' it can be iterated on to execute subsequent requests. Raises: SlackApiError: The following Slack API call failed: 'chat.postMessage'. SlackRequestError: Json data can only be submitted as POST requests. """ if json is not None and http_verb != "POST": msg = "Json data can only be submitted as POST requests. GET requests should use the 'params' argument." raise err.SlackRequestError(msg) api_url = self._get_url(api_method) headers = { "User-Agent": self._get_user_agent(), "Authorization": "Bearer {}".format(self.token), } if files is not None: form_data = aiohttp.FormData() for k, v in files.items(): if isinstance(v, str): form_data.add_field(k, open(v, "rb")) else: form_data.add_field(k, v) if data is not None: for k, v in data.items(): form_data.add_field(k, str(v)) data = form_data req_args = { "headers": headers, "data": data, "params": params, "json": json, "ssl": self.ssl, "proxy": self.proxy, } if self._event_loop is None: self._set_event_loop() future = asyncio.ensure_future( self._send(http_verb=http_verb, api_url=api_url, req_args=req_args), loop=self._event_loop, ) if self.run_async or self._event_loop.is_running(): return future return self._event_loop.run_until_complete(future)