diff options
author | Trygve Aaberge <trygveaa@gmail.com> | 2023-08-20 21:09:10 +0200 |
---|---|---|
committer | Trygve Aaberge <trygveaa@gmail.com> | 2024-02-18 11:32:53 +0100 |
commit | f3f97e5cbb97ed7a04bc3e40a110805ff0ae53c8 (patch) | |
tree | d6abf2212800d82e3e845a4ec5f70dd031fa5f94 /slack | |
parent | 9a92344a77731f443d19dfdb24a6a47a1b7dc9ce (diff) | |
download | wee-slack-f3f97e5cbb97ed7a04bc3e40a110805ff0ae53c8.tar.gz |
Render reactions after messages
Diffstat (limited to 'slack')
-rw-r--r-- | slack/config.py | 17 | ||||
-rw-r--r-- | slack/slack_conversation.py | 21 | ||||
-rw-r--r-- | slack/slack_message.py | 89 | ||||
-rw-r--r-- | slack/slack_user.py | 6 | ||||
-rw-r--r-- | slack/slack_workspace.py | 36 | ||||
-rw-r--r-- | slack/util.py | 4 |
6 files changed, 151 insertions, 22 deletions
diff --git a/slack/config.py b/slack/config.py index 0b717fa..a3b01ce 100644 --- a/slack/config.py +++ b/slack/config.py @@ -76,11 +76,17 @@ class SlackConfigSectionColor: self.reaction_suffix = WeeChatOption( self._section, "reaction_suffix", - "text color for the [:wave:(@user)] suffix on messages that have" - " reactions attached to them.", + "color for the reactions after a message", WeeChatColor("darkgray"), ) + self.reaction_self_suffix = WeeChatOption( + self._section, + "reaction_self_suffix", + "color for the reactions after a message, for reactions you have added", + WeeChatColor("blue"), + ) + self.user_mention_color = WeeChatOption( self._section, "user_mention_color", @@ -116,6 +122,13 @@ class SlackConfigSectionLook: callback_change=self.config_change_color_nicks_in_nicklist_cb, ) + self.display_reaction_nicks = WeeChatOption( + self._section, + "display_reaction_nicks", + "display the name of the reacting user(s) after each reaction", + False, + ) + self.external_user_suffix = WeeChatOption( self._section, "external_user_suffix", diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py index ddc2054..9b49466 100644 --- a/slack/slack_conversation.py +++ b/slack/slack_conversation.py @@ -375,19 +375,36 @@ class SlackConversation: f"{self.buffer_pointer};off;{user.nick()}", ) + async def rerender_message(self, message: SlackMessage): + modify_buffer_line( + self.buffer_pointer, message.ts, await message.render_message(rerender=True) + ) + async def change_message(self, data: SlackMessageChanged): ts = SlackTs(data["ts"]) message = self._messages.get(ts) if message: message.update_message_json(data["message"]) - modify_buffer_line(self.buffer_pointer, ts, await message.render_message()) + await self.rerender_message(message) async def delete_message(self, data: SlackMessageDeleted): ts = SlackTs(data["deleted_ts"]) message = self._messages.get(ts) if message: message.deleted = True - modify_buffer_line(self.buffer_pointer, ts, await message.render_message()) + await self.rerender_message(message) + + async def reaction_add(self, message_ts: SlackTs, reaction: str, user_id: str): + message = self._messages.get(message_ts) + if message: + message.reaction_add(reaction, user_id) + await self.rerender_message(message) + + async def reaction_remove(self, message_ts: SlackTs, reaction: str, user_id: str): + message = self._messages.get(message_ts) + if message: + message.reaction_remove(reaction, user_id) + await self.rerender_message(message) async def typing_add_user(self, user_id: str, thread_ts: Optional[str]): if not shared.config.look.typing_status_nicks.value: diff --git a/slack/slack_message.py b/slack/slack_message.py index 792b064..64214c5 100644 --- a/slack/slack_message.py +++ b/slack/slack_message.py @@ -15,6 +15,7 @@ from slack.util import with_color if TYPE_CHECKING: from slack_api.slack_conversations_history import SlackMessage as SlackMessageDict + from slack_api.slack_conversations_history import SlackMessageReaction from slack.slack_conversation import SlackConversation from slack.slack_workspace import SlackWorkspace @@ -91,6 +92,32 @@ class SlackMessage: self._rendered_prefix = None self._rendered_message = None + def _get_reaction(self, reaction_name: str): + for reaction in self._message_json.get("reactions", []): + if reaction["name"] == reaction_name: + return reaction + + def reaction_add(self, reaction_name: str, user_id: str): + reaction = self._get_reaction(reaction_name) + if reaction: + if user_id not in reaction["users"]: + reaction["users"].append(user_id) + reaction["count"] += 1 + else: + if "reactions" not in self._message_json: + self._message_json["reactions"] = [] + self._message_json["reactions"].append( + {"name": reaction_name, "users": [user_id], "count": 1} + ) + self._rendered_message = None + + def reaction_remove(self, reaction_name: str, user_id: str): + reaction = self._get_reaction(reaction_name) + if reaction and user_id in reaction["users"]: + reaction["users"].remove(user_id) + reaction["count"] -= 1 + self._rendered_message = None + async def tags(self, backlog: bool = False) -> str: nick = await self._nick(colorize=False, only_nick=True) tags = [f"slack_ts_{self.ts}", f"nick_{nick}"] @@ -173,10 +200,8 @@ class SlackMessage: self._rendered_prefix = await self._render_prefix() return self._rendered_prefix - async def _render_message(self) -> str: - if self._deleted: - return with_color(shared.config.color.deleted_message.value, "(deleted)") - elif self._message_json.get("subtype") in [ + async def _render_message_text(self) -> str: + if self._message_json.get("subtype") in [ "channel_join", "group_join", "channel_leave", @@ -205,16 +230,23 @@ class SlackMessage: return f"{await self._nick()} {text_action} {text_conversation_name}{inviter_text}" else: - text = await self._unfurl_refs(self._message_json["text"]) + return await self._unfurl_refs(self._message_json["text"]) + + async def _render_message(self) -> str: + if self._deleted: + return with_color(shared.config.color.deleted_message.value, "(deleted)") + else: + text = await self._render_message_text() text_edited = ( f" {with_color(shared.config.color.edited_message_suffix.value, '(edited)')}" if self._message_json.get("edited") else "" ) - return text + text_edited + reactions = await self._create_reactions_string() + return text + text_edited + reactions - async def render_message(self) -> str: - if self._rendered_message is not None: + async def render_message(self, rerender: bool = False) -> str: + if self._rendered_message is not None and not rerender: return self._rendered_message self._rendered_message = await self._render_message() return self._rendered_message @@ -278,3 +310,44 @@ class SlackMessage: return match[0] return re_mention.sub(unfurl_ref, message) + + async def _create_reaction_string(self, reaction: SlackMessageReaction) -> str: + if shared.config.look.display_reaction_nicks.value: + # TODO: initialize_items? + users = await gather( + *(self.workspace.users[user_id] for user_id in reaction["users"]) + ) + nicks = ",".join(user.nick() for user in users) + users_str = f"({nicks})" + else: + users_str = len(reaction["users"]) + + reaction_string = f":{reaction['name']}:{users_str}" + + if self.workspace.my_user.id in reaction["users"]: + return with_color( + shared.config.color.reaction_self_suffix.value, + reaction_string, + reset_color=shared.config.color.reaction_suffix.value, + ) + else: + return reaction_string + + async def _create_reactions_string(self) -> str: + reactions = self._message_json.get("reactions", []) + reactions_with_users = [ + reaction for reaction in reactions if len(reaction["users"]) > 0 + ] + reaction_strings = await gather( + *( + self._create_reaction_string(reaction) + for reaction in reactions_with_users + ) + ) + reactions_string = " ".join(reaction_strings) + if reactions_string: + return " " + with_color( + shared.config.color.reaction_suffix.value, f"[{reactions_string}]" + ) + else: + return "" diff --git a/slack/slack_user.py b/slack/slack_user.py index aa84089..8e5bea5 100644 --- a/slack/slack_user.py +++ b/slack/slack_user.py @@ -58,8 +58,12 @@ class SlackUser: return cls(workspace, info_response["user"]) @property + def id(self) -> str: + return self._info["id"] + + @property def is_self(self) -> bool: - return self._info["id"] == self.workspace.my_user._info["id"] + return self.id == self.workspace.my_user.id def nick(self, colorize: bool = False, only_nick: bool = False) -> str: nick = self._name_without_spaces() diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py index 28b9b4b..f721bda 100644 --- a/slack/slack_workspace.py +++ b/slack/slack_workspace.py @@ -26,7 +26,7 @@ from slack.proxy import Proxy from slack.shared import shared from slack.slack_api import SlackApi from slack.slack_conversation import SlackConversation -from slack.slack_message import SlackMessage +from slack.slack_message import SlackMessage, SlackTs from slack.slack_user import SlackBot, SlackUser, SlackUsergroup from slack.task import Future, Task, create_task, gather, run_async from slack.util import get_callback_name @@ -273,13 +273,25 @@ class SlackWorkspace: async def _ws_recv(self, data: SlackRtmMessage): try: - channel_id = "channel" in data and data["channel"] - if channel_id in self.open_conversations: - channel = self.open_conversations[channel_id] + if data["type"] == "hello": + return + elif data["type"] == "reaction_added" or data["type"] == "reaction_removed": + channel_id = data["item"]["channel"] + elif "channel" in data and type(data["channel"]) == str: + channel_id = data["channel"] else: - channel = None + weechat.prnt("", f"\t{self.name} received: {json.dumps(data)}") + return + + channel = self.open_conversations.get(channel_id) + if channel is None: + weechat.prnt( + "", + f"\t{self.name} received for not open conversation, discarding: {json.dumps(data)}", + ) + return - if data["type"] == "message" and channel is not None: + if data["type"] == "message": if "subtype" in data and data["subtype"] == "message_changed": await channel.change_message(data) elif "subtype" in data and data["subtype"] == "message_deleted": @@ -289,7 +301,17 @@ class SlackWorkspace: else: message = SlackMessage(channel, data) await channel.add_message(message) - elif data["type"] == "user_typing" and channel is not None: + elif data["type"] == "reaction_added" and data["item"]["type"] == "message": + await channel.reaction_add( + SlackTs(data["item"]["ts"]), data["reaction"], data["user"] + ) + elif ( + data["type"] == "reaction_removed" and data["item"]["type"] == "message" + ): + await channel.reaction_remove( + SlackTs(data["item"]["ts"]), data["reaction"], data["user"] + ) + elif data["type"] == "user_typing": await channel.typing_add_user(data["user"], data.get("thread_ts")) else: weechat.prnt("", f"\t{self.name} received: {json.dumps(data)}") diff --git a/slack/util.py b/slack/util.py index 8dfc967..e216576 100644 --- a/slack/util.py +++ b/slack/util.py @@ -13,5 +13,5 @@ def get_callback_name(callback: Callable[..., WeechatCallbackReturnType]) -> str return callback_id -def with_color(color: str, string: str): - return f"{weechat.color(color)}{string}{weechat.color('reset')}" +def with_color(color: str, string: str, reset_color: str = "reset"): + return f"{weechat.color(color)}{string}{weechat.color(reset_color)}" |