aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slack/config.py7
-rw-r--r--slack/slack_conversation.py31
-rw-r--r--slack/slack_message.py34
-rw-r--r--typings/slack_api/slack_conversations_replies.pyi31
-rw-r--r--typings/slack_rtm/slack_rtm_message.pyi10
5 files changed, 106 insertions, 7 deletions
diff --git a/slack/config.py b/slack/config.py
index 4503709..9cca910 100644
--- a/slack/config.py
+++ b/slack/config.py
@@ -156,6 +156,13 @@ class SlackConfigSectionLook:
False,
)
+ self.display_thread_replies_in_channel = WeeChatOption(
+ self._section,
+ "display_thread_replies_in_channel",
+ "display thread replies in the parent channel; can be overridden per buffer with the buffer localvar display_thread_replies_in_channel; note that it only takes effect for new messages; note that due to limitations in the Slack API, on load only thread messages for parents that are in the buffer and thread messages in subscribed threads will be displayed (but all thread messages received while connected will be displayed)",
+ False,
+ )
+
self.external_user_suffix = WeeChatOption(
self._section,
"external_user_suffix",
diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py
index 1d89eae..982f06a 100644
--- a/slack/slack_conversation.py
+++ b/slack/slack_conversation.py
@@ -199,6 +199,8 @@ class SlackConversationMessageHashes(Dict[SlackTs, str]):
other_message = self._conversation.get_message(ts_with_same_hash)
if other_message:
run_async(self._conversation.rerender_message(other_message))
+ for reply in other_message.replies:
+ run_async(self._conversation.rerender_message(reply))
self._setitem(key, short_hash)
self._inverse_map[short_hash] = key
@@ -392,6 +394,8 @@ class SlackConversation:
for message in messages:
self._messages[message.ts] = message
+ # TODO: Account for reply messages
+
sender_user_ids = [m.sender_user_id for m in messages if m.sender_user_id]
self.workspace.users.initialize_items(sender_user_ids)
@@ -421,17 +425,32 @@ class SlackConversation:
self.buffer_pointer, "", user.nick(), color, "", "", 1
)
+ def display_thread_replies(self) -> bool:
+ buffer_value = weechat.buffer_get_string(
+ self.buffer_pointer, "localvar_display_thread_replies_in_channel"
+ )
+ if buffer_value:
+ return bool(weechat.config_string_to_boolean(buffer_value))
+ return shared.config.look.display_thread_replies_in_channel.value
+
async def add_new_message(self, message: SlackMessage):
# TODO: Remove old messages
self._messages[message.ts] = message
- if self.history_filled:
- await self.print_message(message)
- else:
- weechat.buffer_set(
- self.buffer_pointer, "hotlist", str(message.priority.value)
- )
+
+ if not message.is_reply or self.display_thread_replies():
+ if self.history_filled:
+ await self.print_message(message)
+ else:
+ weechat.buffer_set(
+ self.buffer_pointer, "hotlist", str(message.priority.value)
+ )
+
+ parent_message = message.parent_message
+ if parent_message:
+ parent_message.replies.append(message)
if message.sender_user_id:
+ # TODO: thread buffers
user = await self.workspace.users[message.sender_user_id]
weechat.hook_signal_send(
"typing_set_nick",
diff --git a/slack/slack_message.py b/slack/slack_message.py
index 3537e1d..1d61950 100644
--- a/slack/slack_message.py
+++ b/slack/slack_message.py
@@ -107,6 +107,7 @@ class SlackMessage:
self._rendered_message = None
self.conversation = conversation
self.ts = SlackTs(message_json["ts"])
+ self.replies: List[SlackMessage] = []
self._deleted = False
@property
@@ -118,6 +119,28 @@ class SlackMessage:
return self.conversation.message_hashes[self.ts]
@property
+ def thread_ts(self) -> Optional[SlackTs]:
+ return (
+ SlackTs(self._message_json["thread_ts"])
+ if "thread_ts" in self._message_json
+ else None
+ )
+
+ @property
+ def is_thread_parent(self) -> bool:
+ return self.thread_ts == self.ts
+
+ @property
+ def is_reply(self) -> bool:
+ return self.thread_ts is not None and not self.is_thread_parent
+
+ @property
+ def parent_message(self) -> Optional[SlackMessage]:
+ if not self.is_reply or self.thread_ts is None:
+ return None
+ return self.conversation.get_message(self.thread_ts)
+
+ @property
def is_bot_message(self) -> bool:
return (
"subtype" in self._message_json
@@ -341,6 +364,7 @@ class SlackMessage:
if self._deleted:
return with_color(shared.config.color.deleted_message.value, "(deleted)")
else:
+ thread_prefix = self._create_thread_prefix()
text = await self._render_message_text()
text_edited = (
f" {with_color(shared.config.color.edited_message_suffix.value, '(edited)')}"
@@ -349,7 +373,7 @@ class SlackMessage:
)
reactions = await self._create_reactions_string()
thread = self._create_thread_string()
- return text + text_edited + reactions + thread
+ return thread_prefix + text + text_edited + reactions + thread
async def render_message(self, rerender: bool = False) -> str:
if self._rendered_message is not None and not rerender:
@@ -517,6 +541,14 @@ class SlackMessage:
else:
return ""
+ def _create_thread_prefix(self) -> str:
+ parent_message = self.parent_message
+ if not parent_message:
+ return ""
+
+ text = f"[{parent_message.hash}]"
+ return with_color(nick_color(str(parent_message.hash)), text) + " "
+
def _create_thread_string(self) -> str:
if "reply_count" not in self._message_json:
return ""
diff --git a/typings/slack_api/slack_conversations_replies.pyi b/typings/slack_api/slack_conversations_replies.pyi
new file mode 100644
index 0000000..4e4fd4c
--- /dev/null
+++ b/typings/slack_api/slack_conversations_replies.pyi
@@ -0,0 +1,31 @@
+from typing import List
+
+from slack_api.slack_common import SlackErrorResponse, SlackResponseMetadata
+from slack_api.slack_conversations_history import (
+ SlackMessageStandardCommon,
+ SlackMessageThreadParentNotSubscribedFinal,
+ SlackMessageThreadParentSubscribedFinal,
+)
+from typing_extensions import Literal, NotRequired, TypedDict, final
+
+class SlackMessageThreadCommon(SlackMessageStandardCommon):
+ thread_ts: str
+
+@final
+class SlackMessageThread(SlackMessageThreadCommon):
+ parent_user_id: str
+
+@final
+class SlackConversationsRepliesSuccessResponse(TypedDict):
+ ok: Literal[True]
+ messages: List[
+ SlackMessageThreadParentNotSubscribedFinal
+ | SlackMessageThreadParentSubscribedFinal
+ | SlackMessageThread
+ ]
+ has_more: bool
+ response_metadata: NotRequired[SlackResponseMetadata]
+
+SlackConversationsRepliesResponse = (
+ SlackConversationsRepliesSuccessResponse | SlackErrorResponse
+)
diff --git a/typings/slack_rtm/slack_rtm_message.pyi b/typings/slack_rtm/slack_rtm_message.pyi
index 65f15e8..64dc64e 100644
--- a/typings/slack_rtm/slack_rtm_message.pyi
+++ b/typings/slack_rtm/slack_rtm_message.pyi
@@ -14,6 +14,7 @@ from slack_api.slack_conversations_history import (
SlackMessageThreadParentSubscribed,
SlackMessageWithFiles,
)
+from slack_api.slack_conversations_replies import SlackMessageThreadCommon
from typing_extensions import Literal, NotRequired, TypedDict, final
class SlackRtmHello(TypedDict):
@@ -40,6 +41,14 @@ class SlackMessageThreadParentSubscribedRtm(SlackMessageThreadParentSubscribed):
channel: str
@final
+class SlackMessageThreadRtm(SlackMessageThreadCommon):
+ channel: str
+ event_ts: str
+ source_team: str
+ suppress_notification: bool
+ user_team: str
+
+@final
class SlackMessageWithFilesRtm(SlackMessageWithFiles):
channel: str
@@ -160,6 +169,7 @@ SlackMessageRtm = (
| SlackMessageMeRtm
| SlackMessageThreadParentNotSubscribedRtm
| SlackMessageThreadParentSubscribedRtm
+ | SlackMessageThreadRtm
| SlackMessageWithFilesRtm
| SlackMessageSubtypeHuddleThreadRtm
| SlackMessageSubtypeBotMessageRtm