Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Add] Implement Slack Socket Mode support #1436

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions backend/chainlit/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ async def watch_files_for_changes():

discord_task = asyncio.create_task(client.start(discord_bot_token))

slack_task = None

# Slack Socket Handler if env variable SLACK_WEBSOCKET_TOKEN is set
if os.environ.get("SLACK_BOT_TOKEN") and os.environ.get("SLACK_WEBSOCKET_TOKEN"):
from chainlit.slack.app import start_socket_mode
slack_task = asyncio.create_task(start_socket_mode())

try:
yield
finally:
Expand All @@ -144,6 +151,10 @@ async def watch_files_for_changes():
if discord_task:
discord_task.cancel()
await discord_task

if slack_task:
slack_task.cancel()
await slack_task
except asyncio.exceptions.CancelledError:
pass

Expand Down Expand Up @@ -231,10 +242,10 @@ def get_build_dir(local_target: str, packaged_target: str) -> str:


# -------------------------------------------------------------------------------
# SLACK HANDLER
# SLACK HTTP HANDLER
# -------------------------------------------------------------------------------

if os.environ.get("SLACK_BOT_TOKEN") and os.environ.get("SLACK_SIGNING_SECRET"):
if os.environ.get("SLACK_BOT_TOKEN") and os.environ.get("SLACK_SIGNING_SECRET") and not os.environ.get("SLACK_WEBSOCKET_TOKEN"):
from chainlit.slack.app import slack_app_handler

@router.post("/slack/events")
Expand Down
11 changes: 11 additions & 0 deletions backend/chainlit/slack/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from chainlit.user import PersistedUser, User
from chainlit.user_session import user_session
from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
from slack_bolt.adapter.socket_mode.async_handler import AsyncSocketModeHandler
from slack_bolt.async_app import AsyncApp


Expand Down Expand Up @@ -126,6 +127,16 @@ async def update_step(self, step_dict: StepDict):
)


async def start_socket_mode():
"""
Initializes and starts the Slack app in Socket Mode asynchronously.

Uses the SLACK_WEBSOCKET_TOKEN from environment variables to authenticate.
"""
handler = AsyncSocketModeHandler(slack_app, os.environ.get("SLACK_WEBSOCKET_TOKEN"))
await handler.start_async()


@trace
def init_slack_context(
session: HTTPSession,
Expand Down
18 changes: 18 additions & 0 deletions backend/chainlit/slack_websocket_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This is a simple example to test the slack websocket handler.
# To initiate the websocket dont forget to set the variables:
# - SLACK_BOT_TOKEN
# - SLACK_SIGNING_SECRET
# - SLACK_WEBSOCKET_TOKEN <- this one dictates if websocket or http handler

from chainlit import Message, on_message, user_session


@on_message
async def main(message: Message):
client_type = user_session.get("client_type")
if client_type == "slack":
user_email = user_session.get("user").metadata.get("email")
print(f"Received a message from: {user_email}")
await Message(
content=f"Hi {user_email}, I have received the following message:\n{message.content}",
).send()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really great that you've added this script to facilitate testing, but this really isn't the place for it!

Rather, we have several designated places for such functionality:

  1. The chainlit cookbook. It would be great to have a minimal example of slack bot integration there! In addition, it's also a great guidance for users to get started with this!
  2. Unit tests. It would be absolutely amazing to have a unit test for Slack -- both the legacy behaviour as well as websocket behaviour.
  3. E2E tests, they live in https://github.com/Chainlit/chainlit/tree/main/cypress/e2e. But I don't think it's relevant for Slack as I don't think there's a browser UX in this case!

The way to test this is to monkeypatch chainlit.slack.app.AsyncSocketModeHandler and AsyncApp with spec=AsyncSocketModeHandler and AsyncApp, respectively.

You could then test:

  1. Whether all Slack functions are properly called by calling assertions on the mock.
  2. Where relevant, manually call Slack's callback hooks as if the user sends messages.

For now, just having any testing for Slack integration would be a huuuuuuge contrib. If you can't make it, that's okay, just remove this test file and file a PR for the cookbook with it, as well as a PR for the docs discussing this new behaviour.