aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slack/commands.py24
-rw-r--r--slack/register.py13
-rw-r--r--slack/shared.py1
-rw-r--r--slack/slack_api.py26
-rw-r--r--slack/slack_buffer.py23
-rw-r--r--slack/slack_conversation.py5
-rw-r--r--slack/slack_thread.py15
-rw-r--r--slack/slack_workspace.py10
-rw-r--r--typings/slack_api/slack_common.pyi5
9 files changed, 117 insertions, 5 deletions
diff --git a/slack/commands.py b/slack/commands.py
index 7315359..476c4ba 100644
--- a/slack/commands.py
+++ b/slack/commands.py
@@ -18,7 +18,7 @@ from slack.slack_conversation import SlackConversation
from slack.slack_thread import SlackThread
from slack.slack_user import name_from_user_info_without_spaces
from slack.slack_workspace import SlackWorkspace
-from slack.task import run_async
+from slack.task import run_async, sleep
from slack.util import get_callback_name, with_color
from slack.weechat_config import WeeChatOption, WeeChatOptionTypes
@@ -532,6 +532,19 @@ def complete_previous(slack_buffer: SlackBuffer, query: str) -> int:
return weechat.WEECHAT_RC_OK
+async def mark_read(slack_buffer: SlackBuffer):
+ # Sleep so the read marker is updated before we run slack_buffer.mark_read
+ await sleep(1)
+ await slack_buffer.mark_read()
+
+
+def buffer_set_unread_cb(data: str, buffer: str, command: str) -> int:
+ slack_buffer = shared.buffers.get(buffer)
+ if slack_buffer:
+ run_async(mark_read(slack_buffer))
+ return weechat.WEECHAT_RC_OK
+
+
def input_complete_cb(data: str, buffer: str, command: str) -> int:
slack_buffer = shared.buffers.get(buffer)
if slack_buffer:
@@ -558,6 +571,15 @@ def input_complete_cb(data: str, buffer: str, command: str) -> int:
def register_commands():
weechat.hook_command_run(
+ "/buffer set unread", get_callback_name(buffer_set_unread_cb), ""
+ )
+ weechat.hook_command_run(
+ "/buffer set unread *", get_callback_name(buffer_set_unread_cb), ""
+ )
+ weechat.hook_command_run(
+ "/input set_unread_current_buffer", get_callback_name(buffer_set_unread_cb), ""
+ )
+ weechat.hook_command_run(
"/input complete_*", get_callback_name(input_complete_cb), ""
)
weechat.hook_completion(
diff --git a/slack/register.py b/slack/register.py
index 5e81792..2e85fcc 100644
--- a/slack/register.py
+++ b/slack/register.py
@@ -20,9 +20,18 @@ def shutdown_cb():
def signal_buffer_switch_cb(data: str, signal: str, buffer_pointer: str) -> int:
+ prev_buffer_pointer = shared.current_buffer_pointer
+ shared.current_buffer_pointer = buffer_pointer
+
+ if prev_buffer_pointer != buffer_pointer:
+ prev_slack_buffer = shared.buffers.get(prev_buffer_pointer)
+ if prev_slack_buffer:
+ run_async(prev_slack_buffer.mark_read())
+
slack_buffer = shared.buffers.get(buffer_pointer)
if slack_buffer:
run_async(slack_buffer.buffer_switched_to())
+
return weechat.WEECHAT_RC_OK
@@ -105,6 +114,7 @@ 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.workspaces = {}
shared.config = SlackConfig()
@@ -115,9 +125,6 @@ def register():
"buffer_switch", get_callback_name(signal_buffer_switch_cb), ""
)
weechat.hook_signal(
- "window_switch", get_callback_name(signal_buffer_switch_cb), ""
- )
- weechat.hook_signal(
"input_text_changed", get_callback_name(input_text_changed_cb), ""
)
weechat.hook_signal(
diff --git a/slack/shared.py b/slack/shared.py
index 0cf25f4..1661a04 100644
--- a/slack/shared.py
+++ b/slack/shared.py
@@ -25,6 +25,7 @@ class Shared:
self.active_futures: Dict[str, Future[object]] = {}
self.buffers: Dict[str, SlackBuffer] = {}
self.workspaces: Dict[str, SlackWorkspace] = {}
+ self.current_buffer_pointer: str
self.config: SlackConfig
self.uncaught_errors: List[UncaughtError] = []
self.standard_emojis: Dict[str, Emoji]
diff --git a/slack/slack_api.py b/slack/slack_api.py
index 3aa1d57..8e0b28a 100644
--- a/slack/slack_api.py
+++ b/slack/slack_api.py
@@ -14,6 +14,7 @@ from slack.util import chunked
if TYPE_CHECKING:
from slack_api.slack_bots_info import SlackBotInfoResponse, SlackBotsInfoResponse
+ from slack_api.slack_common import SlackGenericResponse
from slack_api.slack_conversations_history import SlackConversationsHistoryResponse
from slack_api.slack_conversations_info import SlackConversationsInfoResponse
from slack_api.slack_conversations_members import SlackConversationsMembersResponse
@@ -49,7 +50,7 @@ class SlackApiCommon:
class SlackEdgeApi(SlackApiCommon):
@property
def is_available(self) -> bool:
- return self.workspace.config.api_token.value.startswith("xoxc-")
+ return self.workspace.token_type == "session"
async def _fetch_edgeapi(self, method: str, params: EdgeParams = {}):
enterprise_id_part = (
@@ -248,3 +249,26 @@ class SlackApi(SlackApiCommon):
if response["ok"] is False:
raise SlackApiError(self.workspace, method, response)
return response
+
+ async def conversations_mark(self, conversation: SlackConversation, ts: SlackTs):
+ method = "conversations.mark"
+ params: Params = {"channel": conversation.id, "ts": ts}
+ response: SlackGenericResponse = await self._fetch(method, params)
+ if response["ok"] is False:
+ raise SlackApiError(self.workspace, method, response, params)
+ return response
+
+ async def subscriptions_thread_mark(
+ self, conversation: SlackConversation, thread_ts: SlackTs, ts: SlackTs
+ ):
+ method = "subscriptions.thread.mark"
+ params: Params = {
+ "channel": conversation.id,
+ "thread_ts": thread_ts,
+ "ts": ts,
+ "read": 1,
+ }
+ 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 e2cdeac..3da8aad 100644
--- a/slack/slack_buffer.py
+++ b/slack/slack_buffer.py
@@ -259,6 +259,29 @@ class SlackBuffer(ABC):
if backlog:
weechat.buffer_set(self.buffer_pointer, "unread", "")
+ def last_read_line_ts(self) -> Optional[SlackTs]:
+ if self.buffer_pointer:
+ own_lines = weechat.hdata_pointer(
+ weechat.hdata_get("buffer"), self.buffer_pointer, "own_lines"
+ )
+ first_line_not_read = weechat.hdata_integer(
+ weechat.hdata_get("lines"), own_lines, "first_line_not_read"
+ )
+ if first_line_not_read:
+ return
+ line = weechat.hdata_pointer(
+ weechat.hdata_get("lines"), own_lines, "last_read_line"
+ )
+ while line:
+ ts = hdata_line_ts(line)
+ if ts:
+ return ts
+ line = weechat.hdata_move(weechat.hdata_get("line"), line, -1)
+
+ @abstractmethod
+ async def mark_read(self) -> None:
+ raise NotImplementedError()
+
def set_unread_and_hotlist(self):
if self.buffer_pointer:
# TODO: Move unread marker to correct position according to last_read for WeeChat >= 4.0.0
diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py
index e290933..aa8c66e 100644
--- a/slack/slack_conversation.py
+++ b/slack/slack_conversation.py
@@ -529,3 +529,8 @@ class SlackConversation(SlackBuffer):
else:
nick = await message.nick(colorize=False, only_nick=True)
self.nicklist_add_user(sender, nick)
+
+ async def mark_read(self):
+ last_read_line_ts = self.last_read_line_ts()
+ if last_read_line_ts and last_read_line_ts != self.last_read:
+ await self._api.conversations_mark(self, last_read_line_ts)
diff --git a/slack/slack_thread.py b/slack/slack_thread.py
index 44a6751..24bdc66 100644
--- a/slack/slack_thread.py
+++ b/slack/slack_thread.py
@@ -91,3 +91,18 @@ class SlackThread(SlackBuffer):
await self.print_history()
self.history_pending = False
+
+ async def mark_read(self):
+ # subscriptions.thread.mark is only available for session tokens
+ if self.workspace.token_type != "session":
+ return
+
+ # last_read can only be set if it exists (which is on threads you're subscribed to)
+ if self.last_read == SlackTs("0.0"):
+ return
+
+ last_read_line_ts = self.last_read_line_ts()
+ if last_read_line_ts and last_read_line_ts != self.last_read:
+ await self._api.subscriptions_thread_mark(
+ self.parent.conversation, self.parent.ts, last_read_line_ts
+ )
diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py
index 59ac415..f202a86 100644
--- a/slack/slack_workspace.py
+++ b/slack/slack_workspace.py
@@ -50,6 +50,7 @@ if TYPE_CHECKING:
from slack_api.slack_users_conversations import SlackUsersConversations
from slack_api.slack_users_info import SlackUserInfo
from slack_rtm.slack_rtm_message import SlackRtmMessage
+ from typing_extensions import Literal
else:
SlackBotInfo = object
SlackConversationsInfo = object
@@ -197,6 +198,15 @@ class SlackWorkspace:
return f"{self.__class__.__name__}('{self.name}')"
@property
+ def token_type(self) -> Literal["oauth", "session", "unknown"]:
+ if self.config.api_token.value.startswith("xoxp-"):
+ return "oauth"
+ elif self.config.api_token.value.startswith("xoxc-"):
+ return "session"
+ else:
+ return "unknown"
+
+ @property
def is_connected(self):
return self._is_connected
diff --git a/typings/slack_api/slack_common.pyi b/typings/slack_api/slack_common.pyi
index 33a4f76..dc7754a 100644
--- a/typings/slack_api/slack_common.pyi
+++ b/typings/slack_api/slack_common.pyi
@@ -1,10 +1,15 @@
from typing_extensions import Literal, TypedDict, final
+class SlackSuccessResponse(TypedDict):
+ ok: Literal[True]
+
@final
class SlackErrorResponse(TypedDict):
ok: Literal[False]
error: str
+SlackGenericResponse = SlackSuccessResponse | SlackErrorResponse
+
@final
class SlackResponseMetadata(TypedDict):
next_cursor: str