diff options
-rw-r--r-- | slack/config.py | 25 | ||||
-rw-r--r-- | slack/slack_api.py | 9 | ||||
-rw-r--r-- | slack/slack_conversation.py | 20 | ||||
-rw-r--r-- | slack/slack_message.py | 29 | ||||
-rw-r--r-- | slack/slack_workspace.py | 15 | ||||
-rw-r--r-- | typings/slack_api/slack_users_prefs.pyi | 16 | ||||
-rw-r--r-- | typings/slack_rtm/slack_rtm_message.pyi | 7 |
7 files changed, 115 insertions, 6 deletions
diff --git a/slack/config.py b/slack/config.py index 36e5726..10cc0a6 100644 --- a/slack/config.py +++ b/slack/config.py @@ -6,7 +6,7 @@ import weechat from slack.log import print_error from slack.shared import shared -from slack.slack_conversation import invalidate_nicklists +from slack.slack_conversation import invalidate_nicklists, update_buffer_props from slack.slack_workspace import SlackWorkspace from slack.util import get_callback_name from slack.weechat_config import ( @@ -25,6 +25,14 @@ class SlackConfigSectionColor: def __init__(self, weechat_config: WeeChatConfig): self._section = WeeChatSection(weechat_config, "color") + self.buflist_muted_conversation = WeeChatOption( + self._section, + "buflist_muted_conversation", + "text color for muted conversations in the buflist", + WeeChatColor("darkgray"), + callback_change=self.config_change_buflist_muted_conversation_cb, + ) + self.channel_mention = WeeChatOption( self._section, "channel_mention", @@ -111,6 +119,11 @@ class SlackConfigSectionColor: WeeChatColor("blue"), ) + def config_change_buflist_muted_conversation_cb( + self, option: WeeChatOption[WeeChatOptionType], parent_changed: bool + ): + update_buffer_props() + class SlackConfigSectionLook: def __init__(self, weechat_config: WeeChatConfig): @@ -177,6 +190,16 @@ class SlackConfigSectionLook: "*", ) + self.muted_conversations_notify: WeeChatOption[ + Literal["none", "personal_highlights", "all_highlights", "all"] + ] = WeeChatOption( + self._section, + "muted_conversations_notify", + "notify level to set for messages in muted conversations; none: don't notify for any messages; personal_highlights: only notify for personal highlights, i.e. not @channel and @here; all_highlights: notify for all highlights, but not other messages; all: notify for all messages, like other channels; note that this doesn't affect messages in threads you are subscribed to or in open thread buffers, those will always notify", + "personal_highlights", + string_values=["none", "personal_highlights", "all_highlights", "all"], + ) + self.render_emoji_as: WeeChatOption[ Literal["emoji", "name", "both"] ] = WeeChatOption( diff --git a/slack/slack_api.py b/slack/slack_api.py index 9f57fdd..be74c2c 100644 --- a/slack/slack_api.py +++ b/slack/slack_api.py @@ -23,6 +23,7 @@ if TYPE_CHECKING: from slack_api.slack_usergroups_info import SlackUsergroupsInfoResponse from slack_api.slack_users_conversations import SlackUsersConversationsResponse from slack_api.slack_users_info import SlackUserInfoResponse, SlackUsersInfoResponse + from slack_api.slack_users_prefs import SlackUsersPrefsGetResponse from slack_edgeapi.slack_usergroups_info import SlackEdgeUsergroupsInfoResponse from slack_edgeapi.slack_users_search import SlackUsersSearchResponse @@ -134,6 +135,14 @@ class SlackApi(SlackApiCommon): raise SlackApiError(self.workspace, method, response) return response + async def fetch_users_get_prefs(self, prefs: Optional[str] = None): + method = "users.prefs.get" + params: Params = {"prefs": prefs} if prefs else {} + response: SlackUsersPrefsGetResponse = await self._fetch(method, params) + if response["ok"] is False: + raise SlackApiError(self.workspace, method, response) + return response + async def fetch_conversations_history(self, conversation: SlackConversation): method = "conversations.history" params: Params = {"channel": conversation.id} diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py index ba76803..abfafa5 100644 --- a/slack/slack_conversation.py +++ b/slack/slack_conversation.py @@ -14,6 +14,7 @@ from slack.slack_message import MessagePriority, SlackMessage, SlackTs from slack.slack_thread import SlackThread from slack.slack_user import SlackBot, SlackUser, nick_color from slack.task import gather, run_async +from slack.util import with_color if TYPE_CHECKING: from slack_api.slack_conversations_info import SlackConversationsInfo @@ -30,6 +31,12 @@ if TYPE_CHECKING: from slack.slack_workspace import SlackWorkspace +def update_buffer_props(): + for workspace in shared.workspaces.values(): + for conversation in workspace.open_conversations.values(): + run_async(conversation.update_buffer_props()) + + def invalidate_nicklists(): for workspace in shared.workspaces.values(): for conversation in workspace.open_conversations.values(): @@ -164,6 +171,10 @@ class SlackConversation(SlackBuffer): self._info["last_read"] = value self.set_unread_and_hotlist() + @property + def muted(self) -> bool: + return self.id in self.workspace.muted_channels + async def sort_key(self) -> str: type_sort_key = { "channel": 0, @@ -228,6 +239,10 @@ class SlackConversation(SlackBuffer): name_without_prefix = await self.name() name = f"{self.name_prefix('full_name')}{name_without_prefix}" short_name = self.name_prefix("short_name") + name_without_prefix + if self.muted: + short_name = with_color( + shared.config.color.buflist_muted_conversation.value, short_name + ) return name, { "short_name": short_name, @@ -324,10 +339,15 @@ class SlackConversation(SlackBuffer): self.hotlist_tss.add(message.ts) if ( self.display_thread_replies() + and ( + not self.muted + or shared.config.look.muted_conversations_notify.value == "all" + ) and message.latest_reply and message.latest_reply > self.last_read and message.latest_reply not in self.hotlist_tss ): + # TODO: Load subscribed threads, so they are added to hotlist for muted channels if they have highlights priority = ( MessagePriority.PRIVATE if self.buffer_type == "private" diff --git a/slack/slack_message.py b/slack/slack_message.py index c1953d3..c51c8b4 100644 --- a/slack/slack_message.py +++ b/slack/slack_message.py @@ -111,6 +111,7 @@ def convert_int_to_roman(num: int) -> str: class MessagePriority(Enum): + NONE = "none" LOW = weechat.WEECHAT_HOTLIST_LOW MESSAGE = weechat.WEECHAT_HOTLIST_MESSAGE PRIVATE = weechat.WEECHAT_HOTLIST_PRIVATE @@ -221,7 +222,7 @@ class PendingMessageItem: else: assert_never(self.item_type) - def should_highlight(self) -> bool: + def should_highlight(self, only_personal: bool) -> bool: if self.item_type == "conversation": return False elif self.item_type == "user": @@ -231,7 +232,7 @@ class PendingMessageItem: return False elif self.item_type == "broadcast": # TODO: figure out how to handle here broadcast - return True + return not only_personal elif self.item_type == "message_nick": return False else: @@ -342,8 +343,22 @@ class SlackMessage: @property def priority(self) -> MessagePriority: - if self.should_highlight(): + if ( + self.conversation.muted + and shared.config.look.muted_conversations_notify.value == "none" + ): + return MessagePriority.NONE + elif self.should_highlight( + self.conversation.muted + and shared.config.look.muted_conversations_notify.value + == "personal_highlights" + ): return MessagePriority.HIGHLIGHT + elif ( + self.conversation.muted + and shared.config.look.muted_conversations_notify.value != "all" + ): + return MessagePriority.NONE elif self.subtype in [ "channel_join", "group_join", @@ -367,6 +382,8 @@ class SlackMessage: return "notify_message" elif priority == MessagePriority.LOW: return None + elif priority == MessagePriority.NONE: + return "notify_none" else: assert_never(priority) @@ -427,12 +444,14 @@ class SlackMessage: reaction["count"] -= 1 self._rendered_message = None - def should_highlight(self) -> bool: + def should_highlight(self, only_personal: bool) -> bool: # TODO: Highlight words from user preferences parsed_message = self._parse_message_text() for item in parsed_message: - if isinstance(item, PendingMessageItem) and item.should_highlight(): + if isinstance(item, PendingMessageItem) and item.should_highlight( + only_personal + ): return True return False diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py index 76c8350..286f5c7 100644 --- a/slack/slack_workspace.py +++ b/slack/slack_workspace.py @@ -12,6 +12,7 @@ from typing import ( Iterable, List, Optional, + Set, Tuple, Type, TypeVar, @@ -193,6 +194,7 @@ class SlackWorkspace: self.users = SlackUsers(self) self.bots = SlackBots(self) self.usergroups = SlackUsergroups(self) + self.muted_channels: Set[str] = set() def __repr__(self): return f"{self.__class__.__name__}({self.name})" @@ -241,6 +243,9 @@ class SlackWorkspace: await self._connect_ws(rtm_connect["url"]) + prefs = await self.api.fetch_users_get_prefs("muted_channels") + self.muted_channels = set(prefs["prefs"]["muted_channels"].split(",")) + if not self.api.edgeapi.is_available: usergroups = await self.api.fetch_usergroups_list() for usergroup in usergroups["usergroups"]: @@ -332,6 +337,16 @@ class SlackWorkspace: try: if data["type"] == "hello": return + elif data["type"] == "pref_change": + if data["name"] == "muted_channels": + new_muted_channels = set(data["value"].split(",")) + changed_channels = self.muted_channels ^ new_muted_channels + self.muted_channels = new_muted_channels + for channel_id in changed_channels: + channel = self.open_conversations.get(channel_id) + if channel: + await channel.update_buffer_props() + return elif data["type"] == "reaction_added" or data["type"] == "reaction_removed": channel_id = data["item"]["channel"] elif ( diff --git a/typings/slack_api/slack_users_prefs.pyi b/typings/slack_api/slack_users_prefs.pyi new file mode 100644 index 0000000..13cc804 --- /dev/null +++ b/typings/slack_api/slack_users_prefs.pyi @@ -0,0 +1,16 @@ +from __future__ import annotations + +from slack_api.slack_common import SlackErrorResponse +from typing_extensions import Literal, TypedDict, final + +@final +class SlackUsersPrefs(TypedDict): + muted_channels: str + # Incomplete + +@final +class SlackUsersPrefsGetSuccessResponse(TypedDict): + ok: Literal[True] + prefs: SlackUsersPrefs + +SlackUsersPrefsGetResponse = SlackUsersPrefsGetSuccessResponse | SlackErrorResponse diff --git a/typings/slack_rtm/slack_rtm_message.pyi b/typings/slack_rtm/slack_rtm_message.pyi index 7f566e2..709f4a5 100644 --- a/typings/slack_rtm/slack_rtm_message.pyi +++ b/typings/slack_rtm/slack_rtm_message.pyi @@ -239,6 +239,12 @@ class SlackUserTyping(TypedDict): id: int user: str +@final +class SlackPrefChange(TypedDict): + type: Literal["pref_change"] + name: str + value: str + SlackMessageRtm = ( SlackMessageStandardRtm | SlackMessageMeRtm @@ -273,4 +279,5 @@ SlackRtmMessage = ( | SlackShRoomJoin | SlackShRoomUpdate | SlackUserTyping + | SlackPrefChange ) |