aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mock_data/slack_conversations_history_channel_public.json298
-rw-r--r--mock_data/slack_conversations_history_im.json85
-rwxr-xr-xscripts/update_mocks.sh3
-rw-r--r--slack/slack_api.py7
-rw-r--r--slack/slack_conversation.py3
-rw-r--r--slack/slack_message.py6
-rw-r--r--typings/slack_api/slack_conversations_history.pyi153
7 files changed, 551 insertions, 4 deletions
diff --git a/mock_data/slack_conversations_history_channel_public.json b/mock_data/slack_conversations_history_channel_public.json
new file mode 100644
index 0000000..15ef66a
--- /dev/null
+++ b/mock_data/slack_conversations_history_channel_public.json
@@ -0,0 +1,298 @@
+{
+ "ok": true,
+ "messages": [
+ {
+ "type": "message",
+ "text": "d",
+ "user": "U7JNGMGEB",
+ "ts": "1673707855.715349",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "AJO9",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "text",
+ "text": "d"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR"
+ },
+ {
+ "client_msg_id": "e77ce1a6-325c-4987-af9c-924fddd79bd4",
+ "type": "message",
+ "text": "c",
+ "user": "U9NJX9J83",
+ "ts": "1672950604.204009",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "WB=v",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "text",
+ "text": "c"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR",
+ "thread_ts": "1672950604.204009",
+ "reply_count": 3,
+ "reply_users_count": 2,
+ "latest_reply": "1673707723.647229",
+ "reply_users": [
+ "U9NJX9J83",
+ "U7JNGMGEB"
+ ],
+ "is_locked": false,
+ "subscribed": true,
+ "last_read": "1673707723.647229"
+ },
+ {
+ "client_msg_id": "e8ea92f9-ebaf-40ef-88b8-f1a829591bb1",
+ "type": "message",
+ "text": "b",
+ "user": "U9NJX9J83",
+ "ts": "1668031072.607969",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "6Hyuz",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "text",
+ "text": "b"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR",
+ "thread_ts": "1668031072.607969",
+ "reply_count": 2,
+ "reply_users_count": 1,
+ "latest_reply": "1672950600.835739",
+ "reply_users": [
+ "U9NJX9J83"
+ ],
+ "is_locked": false,
+ "subscribed": false
+ },
+ {
+ "client_msg_id": "0101c0b1-f56d-41e3-a99a-071391071441",
+ "type": "message",
+ "text": "1",
+ "user": "U9NJX9J83",
+ "ts": "1667398172.542849",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "Ce6",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "text",
+ "text": "1"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR",
+ "thread_ts": "1667398172.542849",
+ "reply_count": 5,
+ "reply_users_count": 1,
+ "latest_reply": "1672950594.369459",
+ "reply_users": [
+ "U9NJX9J83"
+ ],
+ "is_locked": false,
+ "subscribed": true,
+ "last_read": "1672950594.369459"
+ },
+ {
+ "type": "message",
+ "text": "a",
+ "user": "U7JNGMGEB",
+ "ts": "1667127924.150389",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "03jR",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "text",
+ "text": "a"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR"
+ },
+ {
+ "type": "message",
+ "text": "",
+ "files": [
+ {
+ "id": "F049CK2SPAL",
+ "created": 1667059799,
+ "timestamp": 1667059811,
+ "name": "Some_post_title",
+ "title": "Some post title",
+ "mimetype": "application/vnd.slack-docs",
+ "filetype": "docs",
+ "pretty_type": "Arugula",
+ "user": "U9NJX9J83",
+ "user_team": "T0FC8BFQR",
+ "editable": true,
+ "size": 19,
+ "mode": "docs",
+ "is_external": false,
+ "external_type": "",
+ "is_public": true,
+ "public_url_shared": false,
+ "display_as_bot": false,
+ "username": "",
+ "url_private": "https://files.slack.com/files-pri/T0FC8BFQR-F049CK2SPAL/some_post_title",
+ "url_private_download": "https://files.slack.com/files-pri/T0FC8BFQR-F049CK2SPAL/download/some_post_title",
+ "permalink": "https://wee-slack-test.slack.com/files/T0FC8BFQR/F049CK2SPAL",
+ "permalink_public": "https://slack-files.com/T0FC8BFQR-F049CK2SPAL-b2c5d3567b",
+ "preview": "<p>Some content</p>",
+ "editor": null,
+ "last_editor": "U9NJX9J83",
+ "non_owner_editable": false,
+ "updated": 1667059811,
+ "is_starred": false,
+ "has_rich_preview": false,
+ "file_access": "visible",
+ "media_progress": null
+ }
+ ],
+ "upload": false,
+ "user": "U9NJX9J83",
+ "display_as_bot": false,
+ "ts": "1667059824.317919"
+ },
+ {
+ "type": "message",
+ "text": "<https://vg.no>",
+ "user": "U7JNGMGEB",
+ "ts": "1667057550.980779",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "CbQrF",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "link",
+ "url": "https://vg.no"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR",
+ "attachments": [
+ {
+ "from_url": "https://vg.no/",
+ "image_url": "https://1.vgc.no/vgnett-prod/img/vgLogoSquare.png?28042014-1",
+ "image_width": 476,
+ "image_height": 250,
+ "image_bytes": 8471,
+ "service_icon": "https://www.vg.no/gfx/icons/apple-touch-icon-114-precomposed.png",
+ "id": 1,
+ "original_url": "https://vg.no",
+ "fallback": "VG: Nyheter fra Norges mest leste nettavis – VG",
+ "text": "Norges største nettsted. Oppdateres minutt for minutt på siste nytt innen sport, innenriks, utenriks, og underholdning.",
+ "title": "Nyheter fra Norges mest leste nettavis – VG",
+ "title_link": "https://vg.no/",
+ "service_name": "VG"
+ }
+ ]
+ },
+ {
+ "type": "message",
+ "text": "<https://github.com/golang/go/issues/45624>",
+ "user": "U7JNGMGEB",
+ "ts": "1667057502.831129",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "VuZO",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "link",
+ "url": "https://github.com/golang/go/issues/45624"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR",
+ "attachments": [
+ {
+ "from_url": "https://github.com/golang/go/issues/45624",
+ "image_url": "https://opengraph.githubassets.com/b7dc26526af66d71ff9883293c16e50754be7a056dbc0b52ad734491ba60707a/golang/go/issues/45624",
+ "image_width": 500,
+ "image_height": 250,
+ "image_bytes": 98252,
+ "service_icon": "https://a.slack-edge.com/80588/img/unfurl_icons/github.png",
+ "id": 1,
+ "original_url": "https://github.com/golang/go/issues/45624",
+ "fallback": "GitHub: proposal: expression to create pointer to simple types · Issue #45624 · golang/go",
+ "text": "This notion was addressed in #9097, which was shut down rather summarily. Rather than reopen it, let me take another approach. When &amp;S{} was added to the language as a way to construct a pointe...",
+ "title": "proposal: expression to create pointer to simple types · Issue #45624 · golang/go",
+ "title_link": "https://github.com/golang/go/issues/45624",
+ "service_name": "GitHub"
+ }
+ ],
+ "reactions": [
+ {
+ "name": "+1",
+ "users": [
+ "U7JNGMGEB"
+ ],
+ "count": 1
+ }
+ ]
+ }
+ ],
+ "has_more": false,
+ "is_limited": true,
+ "pin_count": 0,
+ "channel_actions_ts": null,
+ "channel_actions_count": 0
+}
diff --git a/mock_data/slack_conversations_history_im.json b/mock_data/slack_conversations_history_im.json
new file mode 100644
index 0000000..83d5a42
--- /dev/null
+++ b/mock_data/slack_conversations_history_im.json
@@ -0,0 +1,85 @@
+{
+ "ok": true,
+ "messages": [
+ {
+ "type": "message",
+ "text": "<mailto:name@example.com|name@example.com>",
+ "user": "U7JNGMGEB",
+ "ts": "1585253989.000200",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "plZ",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "link",
+ "url": "mailto:name@example.com",
+ "text": "name@example.com"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR"
+ },
+ {
+ "type": "message",
+ "text": "_italic",
+ "user": "U7JNGMGEB",
+ "ts": "1584106682.000100",
+ "blocks": [
+ {
+ "type": "rich_text",
+ "block_id": "Moc9",
+ "elements": [
+ {
+ "type": "rich_text_section",
+ "elements": [
+ {
+ "type": "text",
+ "text": "_italic"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "team": "T0FC8BFQR"
+ },
+ {
+ "type": "message",
+ "subtype": "bot_remove",
+ "text": "removed an integration from this channel: <https://wee-slack-test.slack.com/services/BBG98PXA6|incoming-webhook>",
+ "user": "U7JNGMGEB",
+ "bot_id": "BBG98PXA6",
+ "bot_link": "<https://wee-slack-test.slack.com/services/BBG98PXA6|incoming-webhook>",
+ "ts": "1530267481.000186"
+ },
+ {
+ "type": "message",
+ "subtype": "bot_add",
+ "text": "added an integration to this channel: <https://wee-slack-test.slack.com/services/BBG98PXA6|incoming-webhook>",
+ "user": "U7JNGMGEB",
+ "bot_id": "BBG98PXA6",
+ "bot_link": "<https://wee-slack-test.slack.com/services/BBG98PXA6|incoming-webhook>",
+ "ts": "1530267459.000121",
+ "reactions": [
+ {
+ "name": "smile",
+ "users": [
+ "U7JNGMGEB"
+ ],
+ "count": 1
+ }
+ ]
+ }
+ ],
+ "has_more": false,
+ "pin_count": 0,
+ "channel_actions_ts": null,
+ "channel_actions_count": 0
+}
diff --git a/scripts/update_mocks.sh b/scripts/update_mocks.sh
index 18e9092..2ac5887 100755
--- a/scripts/update_mocks.sh
+++ b/scripts/update_mocks.sh
@@ -23,3 +23,6 @@ curl_slack "$api_base/conversations.info?channel=D9N2KD0V6" | jq . > mock_data/s
curl_slack "$api_base/users.info?user=U017V7T2D40" | jq . > mock_data/slack_users_info_person.json
curl_slack "$api_base/users.info?user=UU6635U31" | jq . > mock_data/slack_users_info_bot.json
+
+curl_slack "$api_base/conversations.history?channel=CK4M8EWJE" | jq . > mock_data/slack_conversations_history_channel_public.json
+curl_slack "$api_base/conversations.history?channel=D7HHQR467" | jq . > mock_data/slack_conversations_history_im.json
diff --git a/slack/slack_api.py b/slack/slack_api.py
index ef522d0..3bd75c4 100644
--- a/slack/slack_api.py
+++ b/slack/slack_api.py
@@ -1,13 +1,14 @@
from __future__ import annotations
import json
-from typing import TYPE_CHECKING, Any, Dict, Union
+from typing import TYPE_CHECKING, Dict, Union
from urllib.parse import urlencode
from slack.http import http_request
from slack.shared import shared
if TYPE_CHECKING:
+ from slack_api.slack_conversations_history import SlackConversationsHistoryResponse
from slack_api.slack_conversations_info import SlackConversationsInfoResponse
from slack_api.slack_users_conversations import SlackUsersConversationsResponse
from slack_api.slack_users_info import SlackUsersInfoResponse
@@ -53,7 +54,9 @@ class SlackApi:
return response
return response
- async def fetch_conversations_history(self, conversation: SlackConversation) -> Any:
+ async def fetch_conversations_history(
+ self, conversation: SlackConversation
+ ) -> SlackConversationsHistoryResponse:
return await self._fetch("conversations.history", {"channel": conversation.id})
async def fetch_conversations_info(
diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py
index 21b953b..49b7a2a 100644
--- a/slack/slack_conversation.py
+++ b/slack/slack_conversation.py
@@ -84,6 +84,9 @@ class SlackConversation:
self.history_pending = True
history = await self.api.fetch_conversations_history(self)
+ if history["ok"] is False:
+ # TODO: Handle error
+ return
start = time.time()
messages = [SlackMessage(self, message) for message in history["messages"]]
diff --git a/slack/slack_message.py b/slack/slack_message.py
index 0a7d9d2..abfa403 100644
--- a/slack/slack_message.py
+++ b/slack/slack_message.py
@@ -1,17 +1,19 @@
from __future__ import annotations
import re
-from typing import TYPE_CHECKING, Any, List
+from typing import TYPE_CHECKING, List
from slack.task import gather
if TYPE_CHECKING:
+ from slack_api.slack_conversations_history import SlackMessage as SlackMessageDict
+
from slack.slack_conversation import SlackConversation
from slack.slack_workspace import SlackApi, SlackWorkspace
class SlackMessage:
- def __init__(self, conversation: SlackConversation, message_json: Any):
+ def __init__(self, conversation: SlackConversation, message_json: SlackMessageDict):
self.conversation = conversation
self.ts = message_json["ts"]
self.message_json = message_json
diff --git a/typings/slack_api/slack_conversations_history.pyi b/typings/slack_api/slack_conversations_history.pyi
new file mode 100644
index 0000000..0c486ed
--- /dev/null
+++ b/typings/slack_api/slack_conversations_history.pyi
@@ -0,0 +1,153 @@
+from __future__ import annotations
+
+from typing import List, Literal, NotRequired, TypedDict, final
+
+@final
+class SlackMessageBlockElement(TypedDict):
+ type: str
+ url: NotRequired[str]
+ text: str
+
+@final
+class SlackMessageBlockElementParent(TypedDict):
+ type: str
+ elements: List[SlackMessageBlockElement]
+
+@final
+class SlackMessageBlock(TypedDict):
+ type: str
+ block_id: str
+ elements: List[SlackMessageBlockElementParent]
+
+@final
+class SlackMessageAttachment(TypedDict):
+ from_url: str
+ image_url: str
+ image_width: int
+ image_height: int
+ image_bytes: int
+ service_icon: str
+ id: int
+ original_url: str
+ fallback: str
+ text: str
+ title: str
+ title_link: str
+ service_name: str
+
+@final
+class SlackMessageReaction(TypedDict):
+ name: str
+ users: List[str]
+ count: int
+
+@final
+class SlackMessageFile(TypedDict):
+ id: str
+ created: int
+ timestamp: int
+ name: str
+ title: str
+ mimetype: str
+ filetype: str
+ pretty_type: str
+ user: str
+ user_team: str
+ editable: bool
+ size: int
+ mode: str
+ is_external: bool
+ external_type: str
+ is_public: bool
+ public_url_shared: bool
+ display_as_bot: bool
+ username: str
+ url_private: str
+ url_private_download: str
+ permalink: str
+ permalink_public: str
+ preview: str
+ editor: None
+ last_editor: str
+ non_owner_editable: bool
+ updated: int
+ is_starred: bool
+ has_rich_preview: bool
+ file_access: str
+ media_progress: None
+
+class SlackMessageCommon(TypedDict):
+ type: Literal["message"]
+ text: str
+ user: str
+ ts: str
+ reactions: List[SlackMessageReaction]
+
+class SlackMessageStandard(SlackMessageCommon):
+ client_msg_id: NotRequired[str]
+ blocks: List[SlackMessageBlock]
+ attachments: List[SlackMessageAttachment]
+ team: str
+
+class SlackMessageThreadParentCommon(SlackMessageStandard):
+ thread_ts: str
+ reply_count: int
+ reply_users_count: int
+ latest_reply: str
+ reply_users: List[str]
+ is_locked: bool
+
+@final
+class SlackMessageThreadParentNotSubscribed(SlackMessageThreadParentCommon):
+ subscribed: Literal[False]
+
+@final
+class SlackMessageThreadParentSubscribed(SlackMessageThreadParentCommon):
+ subscribed: Literal[True]
+ last_read: str
+
+class SlackMessageWithFiles(SlackMessageCommon):
+ files: List[SlackMessageFile]
+ upload: bool
+ display_as_bot: bool
+
+# TODO: Add other subtypes
+@final
+class SlackMessageSubtypeBotRemove(SlackMessageCommon):
+ subtype: Literal["bot_remove"]
+ bot_id: str
+ bot_link: str
+
+@final
+class SlackMessageSubtypeBotAdd(SlackMessageCommon):
+ subtype: Literal["bot_add"]
+ bot_id: str
+ bot_link: str
+
+@final
+class SlackConversationsHistoryErrorResponse(TypedDict):
+ ok: Literal[False]
+ error: str
+
+SlackMessage = (
+ SlackMessageStandard
+ | SlackMessageThreadParentNotSubscribed
+ | SlackMessageThreadParentSubscribed
+ | SlackMessageWithFiles
+ | SlackMessageSubtypeBotRemove
+ | SlackMessageSubtypeBotAdd
+)
+
+@final
+class SlackConversationsHistorySuccessResponse(TypedDict):
+ ok: Literal[True]
+ messages: List[SlackMessage]
+ has_more: bool
+ is_limited: NotRequired[bool]
+ pin_count: int
+ channel_actions_ts: None
+ channel_actions_count: int
+
+SlackConversationsHistoryResponse = (
+ SlackConversationsHistorySuccessResponse | SlackConversationsHistoryErrorResponse
+)