aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--slack/slack_api.py20
-rw-r--r--slack/slack_buffer.py20
-rw-r--r--slack/slack_conversation.py7
-rw-r--r--slack/slack_message.py6
-rw-r--r--slack/slack_thread.py8
-rw-r--r--slack/slack_workspace.py9
-rw-r--r--slack/util.py11
7 files changed, 66 insertions, 15 deletions
diff --git a/slack/slack_api.py b/slack/slack_api.py
index be74c2c..7fff321 100644
--- a/slack/slack_api.py
+++ b/slack/slack_api.py
@@ -297,3 +297,23 @@ class SlackApi(SlackApiCommon):
if response["ok"] is False:
raise SlackApiError(self.workspace, method, response, params)
return response
+
+ async def chat_post_message(
+ self,
+ conversation: SlackConversation,
+ text: str,
+ thread_ts: Optional[SlackTs] = None,
+ ):
+ method = "chat.postMessage"
+ params: Params = {
+ "channel": conversation.id,
+ "text": text,
+ "as_user": True,
+ "link_names": True,
+ }
+ if thread_ts is not None:
+ params["thread_ts"] = thread_ts
+ 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 adad7b7..4fa5981 100644
--- a/slack/slack_buffer.py
+++ b/slack/slack_buffer.py
@@ -9,12 +9,14 @@ import weechat
from slack.shared import shared
from slack.slack_message import SlackMessage, SlackTs
-from slack.util import get_callback_name
+from slack.task import run_async
+from slack.util import get_callback_name, htmlescape
if TYPE_CHECKING:
from typing_extensions import Literal
from slack.slack_api import SlackApi
+ from slack.slack_conversation import SlackConversation
from slack.slack_workspace import SlackWorkspace
@@ -171,6 +173,11 @@ class SlackBuffer(ABC):
@property
@abstractmethod
+ def conversation(self) -> SlackConversation:
+ raise NotImplementedError()
+
+ @property
+ @abstractmethod
def context(self) -> Literal["conversation", "thread"]:
raise NotImplementedError()
@@ -304,8 +311,17 @@ class SlackBuffer(ABC):
weechat.buffer_set(self.buffer_pointer, "hotlist", "-1")
self.hotlist_tss.clear()
+ @abstractmethod
+ async def post_message(self, text: str) -> None:
+ raise NotImplementedError()
+
+ async def process_input(self, input_data: str):
+ 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:
- weechat.prnt(buffer, "Text: %s" % input_data)
+ run_async(self.process_input(input_data))
return weechat.WEECHAT_RC_OK
def _buffer_close_cb(self, data: str, buffer: str) -> int:
diff --git a/slack/slack_conversation.py b/slack/slack_conversation.py
index abfafa5..c1db29e 100644
--- a/slack/slack_conversation.py
+++ b/slack/slack_conversation.py
@@ -140,6 +140,10 @@ class SlackConversation(SlackBuffer):
return self._workspace
@property
+ def conversation(self) -> SlackConversation:
+ return self
+
+ @property
def context(self) -> Literal["conversation", "thread"]:
return "conversation"
@@ -630,3 +634,6 @@ class SlackConversation(SlackBuffer):
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)
+
+ async def post_message(self, text: str) -> None:
+ await self._api.chat_post_message(self.conversation, text)
diff --git a/slack/slack_message.py b/slack/slack_message.py
index ddb2ceb..c986edd 100644
--- a/slack/slack_message.py
+++ b/slack/slack_message.py
@@ -19,7 +19,7 @@ from slack.python_compatibility import removeprefix, removesuffix
from slack.shared import shared
from slack.slack_user import SlackBot, SlackUser, format_bot_nick, nick_color
from slack.task import gather
-from slack.util import intersperse, with_color
+from slack.util import intersperse, unhtmlescape, with_color
if TYPE_CHECKING:
from slack_api.slack_conversations_history import SlackMessage as SlackMessageDict
@@ -42,10 +42,6 @@ if TYPE_CHECKING:
from slack.slack_workspace import SlackWorkspace
-def unhtmlescape(text: str) -> str:
- return text.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&")
-
-
def format_date(timestamp: int, token_string: str, link: Optional[str] = None) -> str:
ref_datetime = datetime.fromtimestamp(timestamp)
link_suffix = f" ({link})" if link else ""
diff --git a/slack/slack_thread.py b/slack/slack_thread.py
index 79ef68c..8033ff1 100644
--- a/slack/slack_thread.py
+++ b/slack/slack_thread.py
@@ -10,6 +10,7 @@ from slack.task import gather
if TYPE_CHECKING:
from typing_extensions import Literal
+ from slack.slack_conversation import SlackConversation
from slack.slack_workspace import SlackWorkspace
@@ -23,6 +24,10 @@ class SlackThread(SlackBuffer):
return self.parent.workspace
@property
+ def conversation(self) -> SlackConversation:
+ return self.parent.conversation
+
+ @property
def context(self) -> Literal["conversation", "thread"]:
return "thread"
@@ -116,3 +121,6 @@ class SlackThread(SlackBuffer):
await self._api.subscriptions_thread_mark(
self.parent.conversation, self.parent.ts, last_read_line_ts
)
+
+ async def post_message(self, text: str) -> None:
+ await self._api.chat_post_message(self.conversation, text, self.parent.ts)
diff --git a/slack/slack_workspace.py b/slack/slack_workspace.py
index 286f5c7..0f9ddfb 100644
--- a/slack/slack_workspace.py
+++ b/slack/slack_workspace.py
@@ -445,16 +445,9 @@ class SlackWorkspace:
if self._ws is None:
raise SlackError(self, "is_connected is True while _ws is None")
- if isinstance(buffer, SlackConversation):
- conversation_id = buffer.id
- elif isinstance(buffer, SlackThread):
- conversation_id = buffer.parent.conversation.id
- else:
- raise NotImplementedError(f"Unknown buffer type: {type(buffer)}")
-
msg = {
"type": "user_typing",
- "channel": conversation_id,
+ "channel": buffer.conversation.id,
}
if isinstance(buffer, SlackThread):
msg["thread_ts"] = buffer.parent.ts
diff --git a/slack/util.py b/slack/util.py
index 41993c5..c988f1f 100644
--- a/slack/util.py
+++ b/slack/util.py
@@ -34,6 +34,17 @@ def with_color(color: Optional[str], string: str, reset_color: str = "reset"):
return string
+# Escape chars that have special meaning to Slack. Note that we do not
+# (and should not) perform full HTML entity-encoding here.
+# See https://api.slack.com/reference/surfaces/formatting#escaping for details.
+def htmlescape(text: str) -> str:
+ return text.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
+
+
+def unhtmlescape(text: str) -> str:
+ return text.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&")
+
+
# From https://github.com/more-itertools/more-itertools/blob/v10.1.0/more_itertools/recipes.py#L93-L106
def take(n: int, iterable: Iterable[T]) -> List[T]:
"""Return first *n* items of the iterable as a list.