Skip to content

Commit

Permalink
Add support for chat.postEphemeral
Browse files Browse the repository at this point in the history
Handle the request
Pass the text to the actor
Accumulate all ephemeral messages into the response
Add to the test

Signed-off-by: Ygal Blum <[email protected]>
  • Loading branch information
ygalblum committed Jul 11, 2024
1 parent 4170336 commit 58dcdef
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 13 deletions.
31 changes: 20 additions & 11 deletions slack_server_mock/actor/actor.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
""" Slack Actor """
import datetime
import json
from typing import List, Tuple

from injector import inject, singleton
from tornado.locks import Event
Expand All @@ -16,6 +17,7 @@ def __init__(self) -> None:
self._websocket = None
self._event = Event()
self._response = None
self._ephemeral_messages = []

def app_connected(self, websocket: WebSocketHandler):
""" Notify the actor that the application connected """
Expand All @@ -29,35 +31,42 @@ def is_app_connected(self):
""" Check if the application is connected """
return self._websocket is not None

async def _wait_for_response(self, timeout: float):
async def _wait_for_response(self, timeout: float) -> Tuple[str, List[str]]:
if timeout < 0:
return

await self._event.wait(
timeout=(None if timeout == 0 else datetime.timedelta(seconds=timeout))
)
response = self._response
try:
await self._event.wait(
timeout=(None if timeout == 0 else datetime.timedelta(seconds=timeout))
)
except util.TimeoutError:
# In case of timeout make sure to return ephemeral_messages that were accumulated
pass

response = self._response or ""
self._response = None
return response
ephemeral_messages = self._ephemeral_messages
self._ephemeral_messages = []
return response, ephemeral_messages

async def send_message(self, msg: str, timeout: float = 300.0) -> str:
async def send_message(self, msg: str, timeout: float = 300.0) -> Tuple[str, List[str]]:
""" Send a message to the application """
if not self.is_app_connected():
raise WebSocketClosedError()

self._websocket.write_message(self._wrap_message_with_envelope(msg))

try:
return await self._wait_for_response(timeout=timeout)
except util.TimeoutError:
return ""
return await self._wait_for_response(timeout=timeout)

def message_received(self, msg: str):
""" Notify the actor that a message was received """
self._response = msg
self._event.set()
self._event.clear()

def ephemeral_received(self, msg: str):
self._ephemeral_messages.append(msg)

@staticmethod
def _wrap_message_with_envelope(msg: str):
return json.dumps(
Expand Down
2 changes: 1 addition & 1 deletion slack_server_mock/servers/actor/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async def post(self):
self.write({"error": "The application is not connected"})
return

self.write({"answer": response})
self.write({"answer": response[0], "ephemeral": response[1]})


class ConnectedHandler(RequestHandler): # pylint: disable=W0223
Expand Down
23 changes: 23 additions & 0 deletions slack_server_mock/servers/http/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,29 @@ def post(self):
)


class ChatPostEphemeralHandler(BaseSlackHandler): # pylint: disable=W0223
""" Handler for chat.postEphemeral endpoint """

def post(self):
""" Handle post request """
# Validate the request
if not self._is_request_valid():
return
# Get the payload
data = load_json_from_body(self)
if not data:
return
# Notify the actor that a message was received
global_injector.get(Actor).ephemeral_received(data['text'])
# Write back to the application
self.write(
{
"ok": True,
"ts": datetime.timestamp(datetime.now())
}
)


class ConversationsListHandler(BaseSlackHandler): # pylint: disable=W0223
""" Handler for conversations.list endpoint """
def _handle(self):
Expand Down
1 change: 1 addition & 0 deletions slack_server_mock/servers/http/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def __init__(self, settings: Settings) -> None:
(r"/apps.connections.open", handler.AppsConnectionsOpenHandler),
(r"/api.test", handler.ApiTestHandler),
(r"/chat.postMessage", handler.ChatPostMessageHandler),
(r"/chat.postEphemeral", handler.ChatPostEphemeralHandler),
(r"/conversations.join", handler.ConversationsJoinHandler),
(r"/conversations.list", handler.ConversationsListHandler),
]
Expand Down
5 changes: 5 additions & 0 deletions test/slackbot.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ def run(self, block=False):

def _got_message(self, message, say):
print("Got message {}".format(message['text']))
self._handler.app.client.chat_postEphemeral(
channel=message['channel'],
user=message['user'],
text=message['text']
)
say(message['text'])

def is_connected(self) -> bool:
Expand Down
14 changes: 13 additions & 1 deletion test/test_echo.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,17 @@ def bot(podman_container):

def test_echo(bot, podman_container):
msg = "foo"
# Send a message
res = requests.post(url="http://localhost:8080/message", json={"message": msg})
assert res.json().get('answer') == msg

# Assert that the answer exists and that it is an echo of the message
answer = res.json().get('answer')
assert answer is not None
assert answer == msg

# Assert that the one ephemeral message exists and that it is an echo of the message
ephemeral_messages = res.json().get('ephemeral')
assert ephemeral_messages is not None
assert isinstance(ephemeral_messages, list)
assert len(ephemeral_messages) == 1
assert ephemeral_messages[0] == msg

0 comments on commit 58dcdef

Please sign in to comment.