diff options
author | Trygve Aaberge <trygveaa@gmail.com> | 2024-02-18 12:43:43 +0100 |
---|---|---|
committer | Trygve Aaberge <trygveaa@gmail.com> | 2024-02-18 12:55:39 +0100 |
commit | 24e21c91a03f32b43af9178e15df0574256c9ce7 (patch) | |
tree | 02a41eae7786a64c115e5149cc756be8bf79296c | |
parent | c847e56d0b10499a7dc761f04ddfd0d860fd7788 (diff) | |
download | wee-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.py | 12 | ||||
-rw-r--r-- | slack/slack_api.py | 21 | ||||
-rw-r--r-- | slack/slack_user.py | 3 | ||||
-rw-r--r-- | typings/slack_api/slack_profile.pyi | 84 | ||||
-rw-r--r-- | typings/slack_api/slack_users_info.pyi | 74 |
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 |