aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrygve Aaberge <trygveaa@gmail.com>2024-02-18 12:43:43 +0100
committerTrygve Aaberge <trygveaa@gmail.com>2024-02-18 12:55:39 +0100
commit24e21c91a03f32b43af9178e15df0574256c9ce7 (patch)
tree02a41eae7786a64c115e5149cc756be8bf79296c
parentc847e56d0b10499a7dc761f04ddfd0d860fd7788 (diff)
downloadwee-slack-24e21c91a03f32b43af9178e15df0574256c9ce7.tar.gz
Post users.profile.set as a JSON body
It's better to post nested objects as JSON rather than url encoding them, and this fixes some type errors.
-rw-r--r--slack/error.py12
-rw-r--r--slack/slack_api.py21
-rw-r--r--slack/slack_user.py3
-rw-r--r--typings/slack_api/slack_profile.pyi84
-rw-r--r--typings/slack_api/slack_users_info.pyi74
5 files changed, 109 insertions, 85 deletions
diff --git a/slack/error.py b/slack/error.py
index 917eddf..d190978 100644
--- a/slack/error.py
+++ b/slack/error.py
@@ -2,7 +2,7 @@ from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime
-from typing import TYPE_CHECKING, Dict, Mapping, Optional, Sequence, Union
+from typing import TYPE_CHECKING, Dict, Optional
from uuid import uuid4
from slack.python_compatibility import format_exception_only
@@ -40,16 +40,14 @@ class SlackApiError(Exception):
workspace: SlackWorkspace,
method: str,
response: SlackErrorResponse,
- params: Mapping[
- str, Union[str, int, bool, Sequence[str], Sequence[int], Sequence[bool]]
- ] = {},
+ request: object = None,
):
super().__init__(
- f"{self.__class__.__name__}: workspace={workspace}, method='{method}', params={params}, response={response}"
+ f"{self.__class__.__name__}: workspace={workspace}, method='{method}', request={request}, response={response}"
)
self.workspace = workspace
self.method = method
- self.params = params
+ self.request = request
self.response = response
@@ -112,7 +110,7 @@ def store_and_format_uncaught_error(uncaught_error: UncaughtError) -> str:
)
elif isinstance(e, SlackApiError):
return (
- f"Error from Slack API method {e.method} with params {e.params} for workspace "
+ f"Error from Slack API method {e.method} with request {e.request} for workspace "
f"{e.workspace.name}: {e.response} ({stack_msg})"
)
elif isinstance(e, SlackRtmError):
diff --git a/slack/slack_api.py b/slack/slack_api.py
index 83e8962..c54268f 100644
--- a/slack/slack_api.py
+++ b/slack/slack_api.py
@@ -29,13 +29,12 @@ if TYPE_CHECKING:
from slack_api.slack_conversations_members import SlackConversationsMembersResponse
from slack_api.slack_conversations_replies import SlackConversationsRepliesResponse
from slack_api.slack_emoji import SlackEmojiListResponse
+ from slack_api.slack_profile import SlackSetProfile, SlackUsersProfileSetResponse
from slack_api.slack_rtm_connect import SlackRtmConnectResponse
from slack_api.slack_team_info import SlackTeamInfoResponse
from slack_api.slack_usergroups_info import SlackUsergroupsInfoResponse
from slack_api.slack_users_conversations import SlackUsersConversationsResponse
from slack_api.slack_users_info import (
- SlackProfile,
- SlackSetProfile,
SlackUserInfoResponse,
SlackUsersInfoResponse,
)
@@ -142,6 +141,18 @@ class SlackApi(SlackApiCommon):
return response
return response
+ async def _post(self, method: str, body: Mapping[str, object]):
+ url = f"https://api.slack.com/api/{method}"
+ options = self._get_request_options()
+ options["httpheader"] += "\nContent-Type: application/json"
+ options["postfields"] = json.dumps(body)
+ response = await http_request(
+ url,
+ options,
+ self.workspace.config.network_timeout.value * 1000,
+ )
+ return json.loads(response)
+
async def fetch_team_info(self):
method = "team.info"
response: SlackTeamInfoResponse = await self._fetch(method)
@@ -256,10 +267,10 @@ class SlackApi(SlackApiCommon):
async def _set_user_profile(self, profile: SlackSetProfile):
method = "users.profile.set"
- params: Params = {"profile": dict(profile)}
- response: SlackProfile = await self._fetch(method, params)
+ body = {"profile": profile}
+ response: SlackUsersProfileSetResponse = await self._post(method, body)
if response["ok"] is False:
- raise SlackApiError(self.workspace, method, response, params)
+ raise SlackApiError(self.workspace, method, response, body)
return response
async def set_user_status(self, status: str):
diff --git a/slack/slack_user.py b/slack/slack_user.py
index 07a87d2..96696fa 100644
--- a/slack/slack_user.py
+++ b/slack/slack_user.py
@@ -13,8 +13,9 @@ from slack.util import with_color
if TYPE_CHECKING:
from slack_api.slack_bots_info import SlackBotInfo
from slack_api.slack_conversations_history import SlackMessageUserProfile
+ from slack_api.slack_profile import SlackProfile
from slack_api.slack_usergroups_info import SlackUsergroupInfo
- from slack_api.slack_users_info import SlackProfile, SlackUserInfo
+ from slack_api.slack_users_info import SlackUserInfo
from typing_extensions import Literal
from slack.slack_workspace import SlackWorkspace
diff --git a/typings/slack_api/slack_profile.pyi b/typings/slack_api/slack_profile.pyi
new file mode 100644
index 0000000..e02d44e
--- /dev/null
+++ b/typings/slack_api/slack_profile.pyi
@@ -0,0 +1,84 @@
+from __future__ import annotations
+
+from typing import Dict, List, Optional
+
+from slack_api.slack_common import SlackErrorResponse
+from typing_extensions import Literal, NotRequired, TypedDict, final
+
+@final
+class SlackProfileField(TypedDict):
+ value: str
+ alt: str
+
+@final
+class SlackProfileStatusEmojiDisplayInfo(TypedDict):
+ emoji_name: str
+ display_url: str
+ unicode: str
+
+class SlackProfileCommon(TypedDict):
+ title: NotRequired[Optional[str]]
+ phone: NotRequired[Optional[str]]
+ skype: NotRequired[Optional[str]]
+ first_name: NotRequired[Optional[str]]
+ last_name: NotRequired[Optional[str]]
+ real_name: NotRequired[Optional[str]]
+ real_name_normalized: NotRequired[Optional[str]]
+ display_name: NotRequired[Optional[str]]
+ display_name_normalized: NotRequired[Optional[str]]
+ fields: NotRequired[Optional[Dict[str, SlackProfileField]]]
+ status_text: NotRequired[Optional[str]]
+ status_emoji: NotRequired[Optional[str]]
+ status_emoji_display_info: NotRequired[
+ Optional[List[SlackProfileStatusEmojiDisplayInfo]]
+ ]
+ status_expiration: NotRequired[Optional[int]]
+ avatar_hash: NotRequired[Optional[str]]
+ image_original: NotRequired[str]
+ is_custom_image: NotRequired[Optional[bool]]
+ huddle_state: NotRequired[Optional[str]]
+ huddle_state_expiration_ts: NotRequired[Optional[int]]
+ image_24: NotRequired[str]
+ image_32: NotRequired[str]
+ image_48: NotRequired[str]
+ image_72: NotRequired[str]
+ image_192: NotRequired[str]
+ image_512: NotRequired[str]
+ image_1024: NotRequired[str]
+ status_text_canonical: NotRequired[Optional[str]]
+ team: str
+
+@final
+class SlackProfilePerson(SlackProfileCommon):
+ email: NotRequired[Optional[str]]
+
+@final
+class SlackProfileBot(SlackProfileCommon):
+ api_app_id: NotRequired[Optional[str]]
+ always_active: NotRequired[Optional[bool]]
+ bot_id: NotRequired[Optional[str]]
+ image_1024: str
+
+SlackProfile = SlackProfilePerson | SlackProfileBot
+
+@final
+class SlackSetProfile(TypedDict):
+ display_name: NotRequired[Optional[str]]
+ email: NotRequired[Optional[str]]
+ first_name: NotRequired[Optional[str]]
+ last_name: NotRequired[Optional[str]]
+ phone: NotRequired[Optional[str]]
+ pronouns: NotRequired[Optional[str]]
+ real_name: NotRequired[Optional[str]]
+ start_date: NotRequired[Optional[str]]
+ title: NotRequired[Optional[str]]
+ status_emoji: NotRequired[Optional[str]]
+ status_expiration: NotRequired[Optional[int]]
+ status_text: NotRequired[Optional[str]]
+
+@final
+class SlackUsersProfileSetSuccessResponse(TypedDict):
+ ok: Literal[True]
+ profile: SlackProfilePerson
+
+SlackUsersProfileSetResponse = SlackUsersProfileSetSuccessResponse | SlackErrorResponse
diff --git a/typings/slack_api/slack_users_info.pyi b/typings/slack_api/slack_users_info.pyi
index a5f1409..776e334 100644
--- a/typings/slack_api/slack_users_info.pyi
+++ b/typings/slack_api/slack_users_info.pyi
@@ -1,84 +1,14 @@
from __future__ import annotations
-from typing import Dict, Generic, List, Optional, TypeVar
+from typing import Generic, List, TypeVar
from slack_api.slack_common import SlackErrorResponse
+from slack_api.slack_profile import SlackProfileBot, SlackProfilePerson
from typing_extensions import Literal, NotRequired, TypedDict, final
T = TypeVar("T")
@final
-class SlackProfileField(TypedDict):
- value: str
- alt: str
-
-@final
-class SlackSetProfile(TypedDict):
- display_name: NotRequired[Optional[str]]
- email: NotRequired[Optional[str]]
- first_name: NotRequired[Optional[str]]
- last_name: NotRequired[Optional[str]]
- phone: NotRequired[Optional[str]]
- pronouns: NotRequired[Optional[str]]
- real_name: NotRequired[Optional[str]]
- start_date: NotRequired[Optional[str]]
- title: NotRequired[Optional[str]]
- status_emoji: NotRequired[Optional[str]]
- status_expiration: NotRequired[Optional[int]]
- status_text: NotRequired[Optional[str]]
-
-@final
-class SlackProfileStatusEmojiDisplayInfo(TypedDict):
- emoji_name: str
- display_url: str
- unicode: str
-
-class SlackProfileCommon(TypedDict):
- title: NotRequired[Optional[str]]
- phone: NotRequired[Optional[str]]
- skype: NotRequired[Optional[str]]
- first_name: NotRequired[Optional[str]]
- last_name: NotRequired[Optional[str]]
- real_name: NotRequired[Optional[str]]
- real_name_normalized: NotRequired[Optional[str]]
- display_name: NotRequired[Optional[str]]
- display_name_normalized: NotRequired[Optional[str]]
- fields: NotRequired[Optional[Dict[str, SlackProfileField]]]
- status_text: NotRequired[Optional[str]]
- status_emoji: NotRequired[Optional[str]]
- status_emoji_display_info: NotRequired[
- Optional[List[SlackProfileStatusEmojiDisplayInfo]]
- ]
- status_expiration: NotRequired[Optional[int]]
- avatar_hash: NotRequired[Optional[str]]
- image_original: NotRequired[str]
- is_custom_image: NotRequired[Optional[bool]]
- huddle_state: NotRequired[Optional[str]]
- huddle_state_expiration_ts: NotRequired[Optional[int]]
- image_24: NotRequired[str]
- image_32: NotRequired[str]
- image_48: NotRequired[str]
- image_72: NotRequired[str]
- image_192: NotRequired[str]
- image_512: NotRequired[str]
- image_1024: NotRequired[str]
- status_text_canonical: NotRequired[Optional[str]]
- team: str
-
-@final
-class SlackProfilePerson(SlackProfileCommon):
- email: NotRequired[Optional[str]]
-
-@final
-class SlackProfileBot(SlackProfileCommon):
- api_app_id: NotRequired[Optional[str]]
- always_active: NotRequired[Optional[bool]]
- bot_id: NotRequired[Optional[str]]
- image_1024: str
-
-SlackProfile = SlackProfilePerson | SlackProfileBot
-
-@final
class SlackEnterpriseUser(TypedDict):
id: str
enterprise_id: str