diff options
author | Trygve Aaberge <trygveaa@gmail.com> | 2023-10-14 21:42:28 +0200 |
---|---|---|
committer | Trygve Aaberge <trygveaa@gmail.com> | 2024-02-18 11:32:54 +0100 |
commit | 2bf006184804104afe13bb11901c0d8674c3a1e2 (patch) | |
tree | 2c58b64361eb94567b7d9b4922bb9eb8647a3cbf /slack | |
parent | 300845598e26b0441afcd719a5b9e3af3e3aa45b (diff) | |
download | wee-slack-2bf006184804104afe13bb11901c0d8674c3a1e2.tar.gz |
Support adding and removing reactions
Diffstat (limited to 'slack')
-rw-r--r-- | slack/register.py | 3 | ||||
-rw-r--r-- | slack/shared.py | 1 | ||||
-rw-r--r-- | slack/slack_api.py | 25 | ||||
-rw-r--r-- | slack/slack_buffer.py | 62 | ||||
-rw-r--r-- | slack/slack_conversation.py | 2 |
5 files changed, 89 insertions, 4 deletions
diff --git a/slack/register.py b/slack/register.py index 23a4d55..b907c49 100644 --- a/slack/register.py +++ b/slack/register.py @@ -119,6 +119,9 @@ def register(): shared.weechat_version = int(weechat.info_get("version_number", "") or 0) shared.current_buffer_pointer = weechat.current_buffer() shared.standard_emojis = load_standard_emojis() + shared.standard_emojis_inverse = { + value["unicode"]: value for value in shared.standard_emojis.values() + } shared.workspaces = {} shared.config = SlackConfig() shared.config.config_read() diff --git a/slack/shared.py b/slack/shared.py index eeb6df5..32bad27 100644 --- a/slack/shared.py +++ b/slack/shared.py @@ -29,6 +29,7 @@ class Shared: self.config: SlackConfig self.uncaught_errors: List[UncaughtError] = [] self.standard_emojis: Dict[str, Emoji] + self.standard_emojis_inverse: Dict[str, Emoji] self.highlight_tag = "highlight" diff --git a/slack/slack_api.py b/slack/slack_api.py index 7fff321..784bea6 100644 --- a/slack/slack_api.py +++ b/slack/slack_api.py @@ -26,6 +26,7 @@ if TYPE_CHECKING: from slack_api.slack_users_prefs import SlackUsersPrefsGetResponse from slack_edgeapi.slack_usergroups_info import SlackEdgeUsergroupsInfoResponse from slack_edgeapi.slack_users_search import SlackUsersSearchResponse + from typing_extensions import Literal, assert_never from slack.slack_conversation import SlackConversation from slack.slack_workspace import SlackWorkspace @@ -317,3 +318,27 @@ class SlackApi(SlackApiCommon): if response["ok"] is False: raise SlackApiError(self.workspace, method, response, params) return response + + async def reactions_change( + self, + conversation: SlackConversation, + ts: SlackTs, + name: str, + change_type: Literal["+", "-"], + ): + method = ( + "reactions.add" + if change_type == "+" + else "reactions.remove" + if change_type == "-" + else assert_never(change_type) + ) + params: Params = { + "channel": conversation.id, + "timestamp": ts, + "name": name, + } + response: SlackGenericResponse = await self._fetch(method, params) + if response["ok"] is False: + raise SlackApiError(self.workspace, method, response, params) + return response diff --git a/slack/slack_buffer.py b/slack/slack_buffer.py index 4fa5981..7a6faee 100644 --- a/slack/slack_buffer.py +++ b/slack/slack_buffer.py @@ -1,5 +1,6 @@ from __future__ import annotations +import re import time from abc import ABC, abstractmethod from contextlib import contextmanager @@ -7,6 +8,7 @@ from typing import TYPE_CHECKING, Dict, List, Mapping, Optional, Set, Tuple import weechat +from slack.log import print_error from slack.shared import shared from slack.slack_message import SlackMessage, SlackTs from slack.task import run_async @@ -19,6 +21,17 @@ if TYPE_CHECKING: from slack.slack_conversation import SlackConversation from slack.slack_workspace import SlackWorkspace +MESSAGE_ID_REGEX_STRING = r"(?P<msg_id>\d+|\$[0-9a-z]{3,})" +REACTION_PREFIX_REGEX_STRING = rf"{MESSAGE_ID_REGEX_STRING}?(?P<reaction_change>\+|-)" + +EMOJI_CHAR_REGEX_STRING = "(?P<emoji_char>[\U00000080-\U0010ffff]+)" +EMOJI_NAME_REGEX_STRING = ( + ":(?P<emoji_name>[a-z0-9_+-]+(?:::skin-tone-[2-6](?:-[2-6])?)?):" +) +EMOJI_CHAR_OR_NAME_REGEX_STRING = ( + f"({EMOJI_CHAR_REGEX_STRING}|{EMOJI_NAME_REGEX_STRING})" +) + def hdata_line_ts(line_pointer: str) -> Optional[SlackTs]: data = weechat.hdata_pointer(weechat.hdata_get("line"), line_pointer, "data") @@ -311,14 +324,57 @@ class SlackBuffer(ABC): weechat.buffer_set(self.buffer_pointer, "hotlist", "-1") self.hotlist_tss.clear() + def ts_from_hash(self, ts_hash: str) -> Optional[SlackTs]: + return self.conversation.message_hashes.get_ts(ts_hash) + + def ts_from_index(self, index: int) -> Optional[SlackTs]: + lines = weechat.hdata_pointer( + weechat.hdata_get("buffer"), self.buffer_pointer, "lines" + ) + line = weechat.hdata_pointer(weechat.hdata_get("lines"), lines, "last_line") + move = -index + 1 + if move: + line = weechat.hdata_move(weechat.hdata_get("line"), line, move) + return hdata_line_ts(line) + + def ts_from_hash_or_index(self, hash_or_index: str) -> Optional[SlackTs]: + if not hash_or_index: + return self.ts_from_index(1) + elif hash_or_index.isdigit(): + return self.ts_from_index(int(hash_or_index)) + else: + return self.ts_from_hash(hash_or_index) + @abstractmethod async def post_message(self, text: str) -> None: raise NotImplementedError() + async def send_change_reaction( + self, ts: SlackTs, emoji_char: str, change_type: Literal["+", "-"] + ) -> None: + emoji = shared.standard_emojis_inverse.get(emoji_char) + emoji_name = emoji["name"] if emoji else emoji_char + await self._api.reactions_change(self.conversation, ts, emoji_name, change_type) + async def process_input(self, input_data: str): - if input_data.startswith(("//", " ")): - input_data = input_data[1:] - await self.post_message(htmlescape(input_data)) + reaction = re.match( + rf"{REACTION_PREFIX_REGEX_STRING}{EMOJI_CHAR_OR_NAME_REGEX_STRING}\s*$", + input_data, + ) + if reaction: + msg_id = reaction.group("msg_id") + ts = self.ts_from_hash_or_index(msg_id) + if ts is None: + print_error(f"No slack message found for message id or index {msg_id}") + return + emoji = reaction.group("emoji_char") or reaction.group("emoji_name") + reaction_change_type = reaction.group("reaction_change") + if reaction_change_type == "+" or reaction_change_type == "-": + await self.send_change_reaction(ts, emoji, reaction_change_type) + else: + if input_data.startswith(("//", " ")): + input_data = input_data[1:] + await self.post_message(htmlescape(input_data)) def _buffer_input_cb(self, data: str, buffer: str, input_data: str) -> int: run_async(self.process_input(input_data)) diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py index 652c8f2..2ac8f21 100644 --- a/slack/slack_conversation.py +++ b/slack/slack_conversation.py @@ -613,7 +613,7 @@ class SlackConversation(SlackBuffer): ) async def open_thread(self, thread_hash: str, switch: bool = False): - thread_ts = self.message_hashes.get_ts(thread_hash) + thread_ts = self.ts_from_hash(thread_hash) if thread_ts: thread_message = self.messages.get(thread_ts) if thread_message is None: |