aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrygve Aaberge <trygveaa@gmail.com>2024-02-19 21:22:26 +0100
committerTrygve Aaberge <trygveaa@gmail.com>2024-02-20 13:21:38 +0100
commit1b02ac44847570610a8696323adeb1c3c36ae187 (patch)
tree9540630b1e677afcd0f1c17391df93d320fd7ddc
parent23d41cfb9b70f2596436e27f69a8865efe32da8a (diff)
downloadwee-slack-1b02ac44847570610a8696323adeb1c3c36ae187.tar.gz
Add support for focus events (mouse/cursor mode)
-rw-r--r--slack/commands.py79
-rw-r--r--slack/shared.py8
-rw-r--r--slack/slack_buffer.py30
-rw-r--r--slack/slack_message.py14
4 files changed, 114 insertions, 17 deletions
diff --git a/slack/commands.py b/slack/commands.py
index 7a336a4..76125a2 100644
--- a/slack/commands.py
+++ b/slack/commands.py
@@ -24,10 +24,10 @@ import weechat
from slack.error import SlackError, SlackRtmError, UncaughtError
from slack.log import open_debug_buffer, print_error
from slack.python_compatibility import format_exception, removeprefix
-from slack.shared import shared
+from slack.shared import EMOJI_CHAR_OR_NAME_REGEX_STRING, shared
from slack.slack_buffer import SlackBuffer
from slack.slack_conversation import SlackConversation
-from slack.slack_message import SlackTs
+from slack.slack_message import SlackTs, ts_from_tag
from slack.slack_thread import SlackThread
from slack.slack_user import SlackUser
from slack.slack_workspace import SlackWorkspace
@@ -36,7 +36,7 @@ from slack.util import get_callback_name, get_resolved_futures, with_color
from slack.weechat_config import WeeChatOption, WeeChatOptionTypes
if TYPE_CHECKING:
- from typing_extensions import Literal
+ from typing_extensions import Literal, assert_never
Options = Dict[str, Union[str, Literal[True]]]
WeechatCommandCallback = Callable[[str, str], None]
@@ -46,6 +46,8 @@ if TYPE_CHECKING:
T = TypeVar("T")
+focus_events = ("auto", "message", "delete", "linkarchive", "reply", "thread")
+
def print_message_not_found_error(msg_id: str):
if msg_id:
@@ -641,6 +643,53 @@ def buffer_set_unread_cb(data: str, buffer: str, command: str) -> int:
return weechat.WEECHAT_RC_OK
+def focus_event_cb(data: str, signal: str, hashtable: Dict[str, str]) -> int:
+ tags = hashtable["_chat_line_tags"].split(",")
+ for tag in tags:
+ ts = ts_from_tag(tag)
+ if ts is not None:
+ break
+ else:
+ return weechat.WEECHAT_RC_OK
+
+ buffer_pointer = hashtable["_buffer"]
+ slack_buffer = shared.buffers.get(buffer_pointer)
+ if slack_buffer is None:
+ return weechat.WEECHAT_RC_OK
+
+ conversation = _get_conversation_from_buffer(slack_buffer)
+ if conversation is None:
+ return weechat.WEECHAT_RC_OK
+
+ message_hash = f"${conversation.message_hashes[ts]}"
+
+ if data not in focus_events:
+ print_error(f"Unknown focus event: {data}")
+ return weechat.WEECHAT_RC_OK
+
+ if data == "auto":
+ emoji_match = re.match(EMOJI_CHAR_OR_NAME_REGEX_STRING, hashtable["_chat_eol"])
+ if emoji_match is not None:
+ emoji = emoji_match.group("emoji_char") or emoji_match.group("emoji_name")
+ run_async(conversation.send_change_reaction(ts, emoji, "toggle"))
+ else:
+ weechat.command(buffer_pointer, f"/input insert {message_hash}")
+ elif data == "message":
+ weechat.command(buffer_pointer, f"/input insert {message_hash}")
+ elif data == "delete":
+ run_async(conversation.api.chat_delete_message(conversation, ts))
+ elif data == "linkarchive":
+ url = _get_linkarchive_url(slack_buffer, ts)
+ weechat.command(buffer_pointer, f"/input insert {url}")
+ elif data == "reply":
+ weechat.command(buffer_pointer, f"/input insert /reply {message_hash}\\x20")
+ elif data == "thread":
+ run_async(conversation.open_thread(message_hash, switch=True))
+ else:
+ assert_never(data)
+ return weechat.WEECHAT_RC_OK
+
+
def register_commands():
weechat.hook_command_run(
"/buffer set unread", get_callback_name(buffer_set_unread_cb), ""
@@ -663,3 +712,27 @@ def register_commands():
get_callback_name(command_cb),
cmd,
)
+
+ for focus_event in focus_events:
+ weechat.hook_hsignal(
+ f"slack_focus_{focus_event}",
+ get_callback_name(focus_event_cb),
+ focus_event,
+ )
+
+ weechat.key_bind(
+ "mouse",
+ {
+ "@chat(python.*):button2": "hsignal:slack_focus_auto",
+ },
+ )
+ weechat.key_bind(
+ "cursor",
+ {
+ "@chat(python.*):D": "hsignal:slack_focus_delete",
+ "@chat(python.*):L": "hsignal:slack_focus_linkarchive; /cursor stop",
+ "@chat(python.*):M": "hsignal:slack_focus_message; /cursor stop",
+ "@chat(python.*):R": "hsignal:slack_focus_reply; /cursor stop",
+ "@chat(python.*):T": "hsignal:slack_focus_thread; /cursor stop",
+ },
+ )
diff --git a/slack/shared.py b/slack/shared.py
index 0aea81e..b4094cb 100644
--- a/slack/shared.py
+++ b/slack/shared.py
@@ -17,6 +17,14 @@ WeechatCallbackReturnType = Union[int, str, Dict[str, str], None]
MESSAGE_ID_REGEX_STRING = r"(?P<msg_id>\d+|\$[0-9a-z]{3,})"
REACTION_CHANGE_REGEX_STRING = r"(?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})"
+)
+
class Shared:
def __init__(self):
diff --git a/slack/slack_buffer.py b/slack/slack_buffer.py
index edb1815..64c2b83 100644
--- a/slack/slack_buffer.py
+++ b/slack/slack_buffer.py
@@ -19,8 +19,13 @@ from typing import (
import weechat
from slack.log import print_error
-from slack.shared import MESSAGE_ID_REGEX_STRING, REACTION_CHANGE_REGEX_STRING, shared
-from slack.slack_message import SlackMessage, SlackTs
+from slack.shared import (
+ EMOJI_CHAR_OR_NAME_REGEX_STRING,
+ MESSAGE_ID_REGEX_STRING,
+ REACTION_CHANGE_REGEX_STRING,
+ shared,
+)
+from slack.slack_message import SlackMessage, SlackTs, ts_from_tag
from slack.slack_user import Nick
from slack.task import gather, run_async
from slack.util import get_callback_name, htmlescape
@@ -32,14 +37,6 @@ if TYPE_CHECKING:
from slack.slack_conversation import SlackConversation
from slack.slack_workspace import SlackWorkspace
-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")
@@ -49,8 +46,9 @@ def hdata_line_ts(line_pointer: str) -> Optional[SlackTs]:
tag = weechat.hdata_string(
weechat.hdata_get("line_data"), data, f"{i}|tags_array"
)
- if tag.startswith("slack_ts_"):
- return SlackTs(tag[9:])
+ ts = ts_from_tag(tag)
+ if ts is not None:
+ return ts
return None
@@ -449,10 +447,16 @@ class SlackBuffer(ABC):
)
async def send_change_reaction(
- self, ts: SlackTs, emoji_char: str, change_type: Literal["+", "-"]
+ self, ts: SlackTs, emoji_char: str, change_type: Literal["+", "-", "toggle"]
) -> None:
emoji = shared.standard_emojis_inverse.get(emoji_char)
emoji_name = emoji["name"] if emoji else emoji_char
+
+ if change_type == "toggle":
+ message = self.messages[ts]
+ has_reacted = message.has_reacted(emoji_name)
+ change_type = "-" if has_reacted else "+"
+
await self.api.reactions_change(self.conversation, ts, emoji_name, change_type)
async def edit_message(self, ts: SlackTs, old: str, new: str, flags: str):
diff --git a/slack/slack_message.py b/slack/slack_message.py
index 30f1ad4..5386f62 100644
--- a/slack/slack_message.py
+++ b/slack/slack_message.py
@@ -49,6 +49,8 @@ if TYPE_CHECKING:
from slack.slack_thread import SlackThread
from slack.slack_workspace import SlackWorkspace
+ts_tag_prefix = "slack_ts_"
+
def format_date(timestamp: int, token_string: str, link: Optional[str] = None) -> str:
ref_datetime = datetime.fromtimestamp(timestamp)
@@ -126,6 +128,12 @@ def convert_int_to_roman(num: int) -> str:
return roman_numeral
+def ts_from_tag(tag: str) -> Optional[SlackTs]:
+ if tag.startswith(ts_tag_prefix):
+ return SlackTs(tag[len(ts_tag_prefix) :])
+ return None
+
+
class MessagePriority(Enum):
NONE = "none"
LOW = weechat.WEECHAT_HOTLIST_LOW
@@ -556,6 +564,10 @@ class SlackMessage:
reaction["count"] -= 1
self._rendered_message = None
+ def has_reacted(self, reaction_name: str) -> bool:
+ reaction = self._get_reaction(reaction_name)
+ return reaction is not None and self.workspace.my_user.id in reaction["users"]
+
def should_highlight(self, only_personal: bool) -> bool:
# TODO: Highlight words from user preferences
parsed_message = self.parse_message_text()
@@ -570,7 +582,7 @@ class SlackMessage:
async def tags(self, backlog: bool) -> str:
nick = await self.nick()
- tags = [f"slack_ts_{self.ts}", f"nick_{nick.raw_nick}"]
+ tags = [f"{ts_tag_prefix}{self.ts}", f"nick_{nick.raw_nick}"]
if self.sender_user_id:
tags.append(f"slack_user_id_{self.sender_user_id}")