aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrygve Aaberge <trygveaa@gmail.com>2023-08-20 12:10:28 +0200
committerTrygve Aaberge <trygveaa@gmail.com>2024-02-18 11:32:53 +0100
commitb4c1ebeead35f84d50dee33a9bf6d314a84a3d86 (patch)
tree47e07e1c04d360168812d47f6dd3703eaa6456bd
parent4e38811c1a97ed4217b8cf38aa0f82816e9f8513 (diff)
downloadwee-slack-b4c1ebeead35f84d50dee33a9bf6d314a84a3d86.tar.gz
Support sending and receiving typing
This does not support globally showing typing from direct messages, like the old wee-slack did, because that's not supported by WeeChats typing plugin. It only shows typing in the current buffer.
-rw-r--r--slack/config.py14
-rw-r--r--slack/register.py11
-rw-r--r--slack/slack_conversation.py27
-rw-r--r--slack/slack_workspace.py21
-rw-r--r--typings/slack_rtm/slack_rtm_message.pyi4
-rw-r--r--typings/websocket.pyi1
6 files changed, 73 insertions, 5 deletions
diff --git a/slack/config.py b/slack/config.py
index e7dbbaf..890d6be 100644
--- a/slack/config.py
+++ b/slack/config.py
@@ -93,6 +93,20 @@ class SlackConfigSectionLook:
"*",
)
+ self.typing_status_nicks = WeeChatOption(
+ self._section,
+ "typing_status_nicks",
+ 'display nicks typing on the channel in bar item "typing" (option typing.look.enabled_nicks must be enabled)',
+ True,
+ )
+
+ self.typing_status_self = WeeChatOption(
+ self._section,
+ "typing_status_self",
+ "send self typing status to channels so that other users see when you are typing a message (option typing.look.enabled_self must be enabled)",
+ True,
+ )
+
weechat.hook_config(
"weechat.look.nick_color_*",
get_callback_name(self.config_change_nick_colors_cb),
diff --git a/slack/register.py b/slack/register.py
index e7628e0..7eab789 100644
--- a/slack/register.py
+++ b/slack/register.py
@@ -68,6 +68,16 @@ def modifier_input_text_display_with_cursor_cb(
return prefix + string
+def typing_self_cb(data: str, signal: str, signal_data: str) -> int:
+ if not shared.config.look.typing_status_self.value:
+ return weechat.WEECHAT_RC_OK
+
+ conversation = get_conversation_from_buffer_pointer(signal_data)
+ if conversation:
+ conversation.typing_update_self(signal)
+ return weechat.WEECHAT_RC_OK
+
+
def ws_ping_cb(data: str, remaining_calls: int) -> int:
for workspace in shared.workspaces.values():
if workspace.is_connected:
@@ -117,6 +127,7 @@ def register():
get_callback_name(modifier_input_text_display_with_cursor_cb),
"",
)
+ weechat.hook_signal("typing_self_*", get_callback_name(typing_self_cb), "")
weechat.hook_timer(5000, 0, 0, get_callback_name(ws_ping_cb), "")
run_async(init_async())
diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py
index fc53979..7c0e90f 100644
--- a/slack/slack_conversation.py
+++ b/slack/slack_conversation.py
@@ -46,6 +46,7 @@ class SlackConversation:
self._info = info
self._members: Optional[List[str]] = None
self._messages: OrderedDict[SlackTs, SlackMessage] = OrderedDict()
+ self._typing_self_last_sent = time.time()
# TODO: buffer_pointer may be accessed by buffer_switch before it's initialized
self.buffer_pointer: str = ""
self.is_loading = False
@@ -251,6 +252,32 @@ class SlackConversation:
self.buffer_pointer, "hotlist", str(message.priority.value)
)
+ if message.sender_user_id:
+ user = await self.workspace.users[message.sender_user_id]
+ weechat.hook_signal_send(
+ "typing_set_nick",
+ weechat.WEECHAT_HOOK_SIGNAL_STRING,
+ f"{self.buffer_pointer};off;{user.nick()}",
+ )
+
+ async def typing_add_user(self, user_id: str, thread_ts: Optional[str]):
+ if not shared.config.look.typing_status_nicks.value:
+ return
+
+ if not thread_ts:
+ user = await self.workspace.users[user_id]
+ weechat.hook_signal_send(
+ "typing_set_nick",
+ weechat.WEECHAT_HOOK_SIGNAL_STRING,
+ f"{self.buffer_pointer};typing;{user.nick()}",
+ )
+
+ def typing_update_self(self, typing_state: str):
+ now = time.time()
+ if now - 4 > self._typing_self_last_sent:
+ self._typing_self_last_sent = now
+ self.workspace.send_typing(self.id)
+
def print_message(self, message: SlackMessage, rendered: str):
weechat.prnt_date_tags(self.buffer_pointer, message.ts.major, "", rendered)
diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py
index 7e001a9..0d9405f 100644
--- a/slack/slack_workspace.py
+++ b/slack/slack_workspace.py
@@ -273,6 +273,12 @@ 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]
+ else:
+ channel = None
+
if data["type"] == "message":
if "subtype" in data and data["subtype"] == "message_changed":
pass
@@ -281,11 +287,12 @@ class SlackWorkspace:
elif "subtype" in data and data["subtype"] == "message_replied":
pass
else:
- channel_id = data["channel"]
- if channel_id in self.open_conversations:
- channel = self.open_conversations[channel_id]
+ if channel:
message = SlackMessage(channel, data)
await channel.add_message(message)
+ elif data["type"] == "user_typing":
+ if channel:
+ await channel.typing_add_user(data["user"], data.get("thread_ts"))
else:
weechat.prnt("", f"\t{self.name} received: {json.dumps(data)}")
except Exception as e:
@@ -304,6 +311,14 @@ class SlackWorkspace:
print("lost connection on ping, reconnecting")
run_async(self.reconnect())
+ def send_typing(self, conversation_id: str):
+ if not self.is_connected:
+ raise SlackError(self, "Can't send typing when not connected")
+ if self._ws is None:
+ raise SlackError(self, "is_connected is True while _ws is None")
+ msg = {"type": "typing", "channel": conversation_id}
+ self._ws.send(json.dumps(msg))
+
async def reconnect(self):
self.disconnect()
await self.connect()
diff --git a/typings/slack_rtm/slack_rtm_message.pyi b/typings/slack_rtm/slack_rtm_message.pyi
index 0f8d397..7eb50f1 100644
--- a/typings/slack_rtm/slack_rtm_message.pyi
+++ b/typings/slack_rtm/slack_rtm_message.pyi
@@ -8,7 +8,7 @@ from slack_api.slack_conversations_history import (
SlackMessageThreadParentSubscribed,
SlackMessageWithFiles,
)
-from typing_extensions import Literal, TypedDict, final
+from typing_extensions import Literal, NotRequired, TypedDict, final
class SlackRtmHello(TypedDict):
type: Literal["hello"]
@@ -81,7 +81,7 @@ class SlackMessageReplied(TypedDict):
class SlackUserTyping(TypedDict):
type: Literal["user_typing"]
channel: str
- thread_ts: str
+ thread_ts: NotRequired[str]
id: int
user: str
diff --git a/typings/websocket.pyi b/typings/websocket.pyi
index 0b9b769..3e5d683 100644
--- a/typings/websocket.pyi
+++ b/typings/websocket.pyi
@@ -51,6 +51,7 @@ class WebSocketConnectionClosedException(WebSocketException): ...
class WebSocket:
sock: socket
+ def send(self, payload: str, opcode: int = ABNF.OPCODE_TEXT) -> int: ...
def ping(self, payload: str = ...) -> None: ...
def recv_data(
self, control_frame: bool