aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrygve Aaberge <trygveaa@gmail.com>2023-01-19 20:40:32 +0100
committerTrygve Aaberge <trygveaa@gmail.com>2024-02-18 11:32:53 +0100
commitd6b4f505d53e69ecb39d518a89d8d0013e22ad51 (patch)
tree2826b143dcf48d078d678255f6f7b0d715dc6c43
parent18f59853a045365b3d6bab26ca5d599b8cbc82db (diff)
downloadwee-slack-d6b4f505d53e69ecb39d518a89d8d0013e22ad51.tar.gz
Print error message on http/api error
-rw-r--r--slack/error.py32
-rw-r--r--slack/http.py10
-rw-r--r--slack/slack_api.py100
-rw-r--r--slack/slack_conversation.py6
-rw-r--r--slack/slack_user.py6
-rw-r--r--slack/slack_workspace.py14
-rw-r--r--slack/task.py18
-rw-r--r--tests/test_http_request.py8
8 files changed, 123 insertions, 71 deletions
diff --git a/slack/error.py b/slack/error.py
new file mode 100644
index 0000000..8a8c1b9
--- /dev/null
+++ b/slack/error.py
@@ -0,0 +1,32 @@
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Mapping, Union
+
+if TYPE_CHECKING:
+ from slack_api.slack_error import SlackErrorResponse
+
+ from slack.slack_workspace import SlackWorkspace
+
+
+class HttpError(Exception):
+ def __init__(self, url: str, return_code: int, http_status_code: int, error: str):
+ super().__init__()
+ self.url = url
+ self.return_code = return_code
+ self.http_status_code = http_status_code
+ self.error = error
+
+
+class SlackApiError(Exception):
+ def __init__(
+ self,
+ workspace: SlackWorkspace,
+ method: str,
+ response: SlackErrorResponse,
+ params: Mapping[str, Union[str, int, bool]] = {},
+ ):
+ super().__init__()
+ self.workspace = workspace
+ self.method = method
+ self.params = params
+ self.response = response
diff --git a/slack/http.py b/slack/http.py
index a0baa74..820abb8 100644
--- a/slack/http.py
+++ b/slack/http.py
@@ -7,20 +7,12 @@ from typing import Dict
import weechat
+from slack.error import HttpError
from slack.log import LogLevel, log
from slack.task import FutureProcess, sleep, weechat_task_cb
from slack.util import get_callback_name
-class HttpError(Exception):
- def __init__(self, url: str, return_code: int, http_status: int, error: str):
- super().__init__()
- self.url = url
- self.return_code = return_code
- self.http_status = http_status
- self.error = error
-
-
def available_file_descriptors():
num_current_file_descriptors = len(os.listdir("/proc/self/fd/"))
max_file_descriptors = min(resource.getrlimit(resource.RLIMIT_NOFILE))
diff --git a/slack/slack_api.py b/slack/slack_api.py
index 492aae4..f68abd1 100644
--- a/slack/slack_api.py
+++ b/slack/slack_api.py
@@ -1,9 +1,10 @@
from __future__ import annotations
import json
-from typing import TYPE_CHECKING, Dict, Iterable, Union
+from typing import TYPE_CHECKING, Iterable, Mapping, Union
from urllib.parse import urlencode
+from slack.error import SlackApiError
from slack.http import http_request
from slack.shared import shared
@@ -18,6 +19,8 @@ if TYPE_CHECKING:
from slack.slack_conversation import SlackConversation
from slack.slack_workspace import SlackWorkspace
+Params = Mapping[str, Union[str, int, bool]]
+
class SlackApi:
def __init__(self, workspace: SlackWorkspace):
@@ -30,7 +33,7 @@ class SlackApi:
"cookie": self.workspace.config.api_cookies.value, # TODO: url_encode_if_not_encoded
}
- async def _fetch(self, method: str, params: Dict[str, Union[str, int]] = {}):
+ async def _fetch(self, method: str, params: Params = {}):
url = f"https://api.slack.com/api/{method}"
options = self._get_request_options()
options["postfields"] = urlencode(params)
@@ -45,30 +48,40 @@ class SlackApi:
self,
method: str,
list_key: str,
- params: Dict[str, Union[str, int]] = {},
+ params: Params = {},
pages: int = -1, # negative or 0 means all pages
):
response = await self._fetch(method, params)
next_cursor = response.get("response_metadata", {}).get("next_cursor")
if pages != 1 and next_cursor and response["ok"]:
- params["cursor"] = next_cursor
- next_pages = await self._fetch_list(method, list_key, params, pages - 1)
+ new_params = {**params, "cursor": next_cursor}
+ next_pages = await self._fetch_list(method, list_key, new_params, pages - 1)
response[list_key].extend(next_pages[list_key])
return response
return response
- async def fetch_rtm_connect(self) -> SlackRtmConnectResponse:
- return await self._fetch("rtm.connect")
+ async def fetch_rtm_connect(self):
+ method = "rtm.connect"
+ response: SlackRtmConnectResponse = await self._fetch(method)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response)
+ return response
- async def fetch_conversations_history(
- self, conversation: SlackConversation
- ) -> SlackConversationsHistoryResponse:
- return await self._fetch("conversations.history", {"channel": conversation.id})
+ async def fetch_conversations_history(self, conversation: SlackConversation):
+ method = "conversations.history"
+ params = {"channel": conversation.id}
+ response: SlackConversationsHistoryResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
- async def fetch_conversations_info(
- self, conversation: SlackConversation
- ) -> SlackConversationsInfoResponse:
- return await self._fetch("conversations.info", {"channel": conversation.id})
+ async def fetch_conversations_info(self, conversation: SlackConversation):
+ method = "conversations.info"
+ params = {"channel": conversation.id}
+ response: SlackConversationsInfoResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
async def fetch_users_conversations(
self,
@@ -76,26 +89,51 @@ class SlackApi:
exclude_archived: bool = True,
limit: int = 1000,
pages: int = -1,
- ) -> SlackUsersConversationsResponse:
- return await self._fetch_list(
- "users.conversations",
+ ):
+ method = "users.conversations"
+ params = {
+ "types": types,
+ "exclude_archived": exclude_archived,
+ "limit": limit,
+ }
+ response: SlackUsersConversationsResponse = await self._fetch_list(
+ method,
"channels",
- {
- "types": types,
- "exclude_archived": exclude_archived,
- "limit": limit,
- },
+ params,
pages,
)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
- async def fetch_user_info(self, user_id: str) -> SlackUserInfoResponse:
- return await self._fetch("users.info", {"user": user_id})
+ async def fetch_user_info(self, user_id: str):
+ method = "users.info"
+ params = {"user": user_id}
+ response: SlackUserInfoResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
- async def fetch_users_info(self, user_ids: Iterable[str]) -> SlackUsersInfoResponse:
- return await self._fetch("users.info", {"users": ",".join(user_ids)})
+ async def fetch_users_info(self, user_ids: Iterable[str]):
+ method = "users.info"
+ params = {"users": ",".join(user_ids)}
+ response: SlackUsersInfoResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
- async def fetch_bot_info(self, bot_id: str) -> SlackBotInfoResponse:
- return await self._fetch("bots.info", {"bot": bot_id})
+ async def fetch_bot_info(self, bot_id: str):
+ method = "bots.info"
+ params = {"bot": bot_id}
+ response: SlackBotInfoResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
- async def fetch_bots_info(self, bot_ids: Iterable[str]) -> SlackBotsInfoResponse:
- return await self._fetch("bots.info", {"bots": ",".join(bot_ids)})
+ async def fetch_bots_info(self, bot_ids: Iterable[str]):
+ method = "bots.info"
+ params = {"bots": ",".join(bot_ids)}
+ response: SlackBotsInfoResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py
index 8123df8..8fb1c0e 100644
--- a/slack/slack_conversation.py
+++ b/slack/slack_conversation.py
@@ -54,9 +54,6 @@ class SlackConversation:
async def init(self):
with self.loading():
info = await self._api.fetch_conversations_info(self)
- if info["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching conversation info")
info_channel = info["channel"]
if info_channel["is_im"] is True:
@@ -84,9 +81,6 @@ class SlackConversation:
self.history_pending = True
history = await self._api.fetch_conversations_history(self)
- if history["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching conversation history")
start = time.time()
messages = [SlackMessage(self, message) for message in history["messages"]]
diff --git a/slack/slack_user.py b/slack/slack_user.py
index 6b9b056..b895872 100644
--- a/slack/slack_user.py
+++ b/slack/slack_user.py
@@ -49,9 +49,6 @@ class SlackUser:
async def _set_info(self):
info_response = await self._api.fetch_user_info(self.id)
- if info_response["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching user info")
self._info = info_response["user"]
async def ensure_initialized(self):
@@ -113,9 +110,6 @@ class SlackBot:
async def _set_info(self):
info_response = await self._api.fetch_bot_info(self.id)
- if info_response["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching bot info")
self._info = info_response["bot"]
async def ensure_initialized(self):
diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py
index 5074174..96740ec 100644
--- a/slack/slack_workspace.py
+++ b/slack/slack_workspace.py
@@ -54,9 +54,6 @@ class SlackUsers(Dict[str, Future[SlackUser]]):
async def _fetch_items_info(self, item_ids: Iterable[str]):
response = await self.workspace.api.fetch_users_info(item_ids)
- if response["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching users")
return {info["id"]: info for info in response["users"]}
@@ -92,9 +89,6 @@ class SlackBots(Dict[str, Future[SlackBot]]):
async def _fetch_items_info(self, item_ids: Iterable[str]):
response = await self.workspace.api.fetch_bots_info(item_ids)
- if response["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching bots")
return {info["id"]: info for info in response["bots"]}
@@ -119,10 +113,6 @@ class SlackWorkspace:
async def connect(self):
rtm_connect = await self.api.fetch_rtm_connect()
- if rtm_connect["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching rtm.connect")
-
self.id = rtm_connect["team"]["id"]
self.my_user = await self.users[rtm_connect["self"]["id"]]
@@ -132,10 +122,6 @@ class SlackWorkspace:
user_channels_response = await self.api.fetch_users_conversations(
"public_channel"
)
- if user_channels_response["ok"] is False:
- # TODO: Handle error
- raise Exception("Failed fetching conversations")
-
user_channels = user_channels_response["channels"]
for channel in user_channels:
diff --git a/slack/task.py b/slack/task.py
index 9ebcad5..ac3677b 100644
--- a/slack/task.py
+++ b/slack/task.py
@@ -15,6 +15,8 @@ from uuid import uuid4
import weechat
+from slack.error import HttpError, SlackApiError
+from slack.log import print_error
from slack.shared import shared
from slack.util import get_callback_name
@@ -74,7 +76,21 @@ def weechat_task_cb(data: str, *args: Any) -> int:
def task_runner(task: Task[Any], response: Any):
while True:
try:
- future = task.coroutine.send(response)
+ try:
+ future = task.coroutine.send(response)
+ except HttpError as e:
+ print_error(
+ f"Error calling URL {e.url}: return code: {e.return_code}, "
+ f"http status code: {e.http_status_code}, error: {e.error}"
+ )
+ return
+ except SlackApiError as e:
+ print_error(
+ f"Error from Slack API method {e.method} for workspace "
+ f"{e.workspace.name}: {e.response}"
+ )
+ return
+
if future.finished:
response = future.result
else:
diff --git a/tests/test_http_request.py b/tests/test_http_request.py
index 300882d..3c74e1f 100644
--- a/tests/test_http_request.py
+++ b/tests/test_http_request.py
@@ -44,7 +44,7 @@ def test_http_request_error_process_return_code():
assert excinfo.value.url == url
assert excinfo.value.return_code == -2
- assert excinfo.value.http_status == 0
+ assert excinfo.value.http_status_code == 0
assert excinfo.value.error == ""
@@ -58,7 +58,7 @@ def test_http_request_error_process_stderr():
assert excinfo.value.url == url
assert excinfo.value.return_code == 0
- assert excinfo.value.http_status == 0
+ assert excinfo.value.http_status_code == 0
assert excinfo.value.error == "err"
@@ -75,7 +75,7 @@ def test_http_request_error_process_http():
assert excinfo.value.url == url
assert excinfo.value.return_code == 0
- assert excinfo.value.http_status == 400
+ assert excinfo.value.http_status_code == 400
assert excinfo.value.error == response
@@ -110,7 +110,7 @@ def test_http_request_error_retry_error():
assert excinfo.value.url == url
assert excinfo.value.return_code == -2
- assert excinfo.value.http_status == 0
+ assert excinfo.value.http_status_code == 0
assert excinfo.value.error == ""