Socket Mode Client
Socket Mode is a method of connecting your app to the Slack APIs using WebSockets instead of HTTP. You can use slack_sdk.socket_mode.SocketModeClient
for managing Socket Mode connections and performing interactions with Slack.
Using Socket Mode
First off, let's start with enabling Socket Mode. Visit the Slack App configuration page, choose the app you're working on, and go to Settings on the left pane. There are a few things to do on the page.
-
Go to Settings > Basic Information, then add a new App-Level Token with the
connections:write
scope -
Go to Settings > Socket Mode, then turn on Enable Socket Mode
-
Go to Features > App Home, look under Show Tabs > Messages Tab then turn on Allow users to send Slash commands and messages from the messages tab
-
Go to Features > Event Subscriptions, then turn on Enable Events
-
On the same page expand Subscribe to bot events click Add Bot User Event and select message.im. This will allow the bot to get events for messages that are sent in 1:1 direct messages with itself
-
Go to Features > Interactivity and Shortcuts, look under Shortcuts* click Create a New Shortcut then create a new Global shortcut with the following details
Name: Hello
Short Description: Receive a Greeting
Callback ID: hello-shortcut
-
Go to Features > OAuth & Permissions under Scopes > Bot Token Scopes click Add an OAuth Scope and select reactions:write. This will allow the bot to add emoji reactions (Reacjis) to messages.
-
Go to Features > Oauth & Permissions under OAuth Tokens for Your Workspace click Install to Workspace
You will be using the app-level token that starts with xapp-
prefix.
Note that the token here is not the ones starting with either xoxb-
or
xoxp-
.
import os
from slack_sdk.web import WebClient
from slack_sdk.socket_mode import SocketModeClient
# Initialize SocketModeClient with an app-level token + WebClient
client = SocketModeClient(
# This app-level token will be used only for establishing a connection
app_token=os.environ.get("SLACK_APP_TOKEN"), # xapp-A111-222-xyz
# You will be using this WebClient for performing Web API calls in listeners
web_client=WebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # xoxb-111-222-xyz
)
from slack_sdk.socket_mode.response import SocketModeResponse
from slack_sdk.socket_mode.request import SocketModeRequest
def process(client: SocketModeClient, req: SocketModeRequest):
if req.type == "events_api":
# Acknowledge the request anyway
response = SocketModeResponse(envelope_id=req.envelope_id)
client.send_socket_mode_response(response)
# Add a reaction to the message if it's a new message
if req.payload["event"]["type"] == "message" \
and req.payload["event"].get("subtype") is None:
client.web_client.reactions_add(
name="eyes",
channel=req.payload["event"]["channel"],
timestamp=req.payload["event"]["ts"],
)
if req.type == "interactive" \
and req.payload.get("type") == "shortcut":
if req.payload["callback_id"] == "hello-shortcut":
# Acknowledge the request
response = SocketModeResponse(envelope_id=req.envelope_id)
client.send_socket_mode_response(response)
# Open a welcome modal
client.web_client.views_open(
trigger_id=req.payload["trigger_id"],
view={
"type": "modal",
"callback_id": "hello-modal",
"title": {
"type": "plain_text",
"text": "Greetings!"
},
"submit": {
"type": "plain_text",
"text": "Good Bye"
},
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hello!"
}
}
]
}
)
if req.type == "interactive" \
and req.payload.get("type") == "view_submission":
if req.payload["view"]["callback_id"] == "hello-modal":
# Acknowledge the request and close the modal
response = SocketModeResponse(envelope_id=req.envelope_id)
client.send_socket_mode_response(response)
# Add a new listener to receive messages from Slack
# You can add more listeners like this
client.socket_mode_request_listeners.append(process)
# Establish a WebSocket connection to the Socket Mode servers
client.connect()
# Just not to stop this process
from threading import Event
Event().wait()
Supported Libraries
This SDK offers its own simple WebSocket client covering only required
features for Socket Mode. In addition to that, SocketModeClient
is
implemented with a few 3rd party open-source libraries. If you prefer
any of the following, you can use it over the built-in one.
PyPI Project | SocketModeClient |
---|---|
slack_sdk | slack_sdk.socket_mode.SocketModeClient |
websocket_client | slack_sdk.socket_mode.websocket_client.SocketModeClient |
aiohttp (asyncio-based) | slack_sdk.socket_mode.aiohttp.SocketModeClient |
websockets (asyncio-based) | slack_sdk.socket_mode.websockets.SocketModeClient |
To use the
websocket_client
based
one, add websocket_client
dependency and to change the import as below.
# Note that the pockage is different
from slack_sdk.socket_mode.websocket_client import SocketModeClient
client = SocketModeClient(
app_token=os.environ.get("SLACK_APP_TOKEN"), # xapp-A111-222-xyz
web_client=WebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # xoxb-111-222-xyz
)
You can pass a few additional arguments that are specific to the library. Apart from that, all the functionalities work in the same way with the built-in version.
Asyncio Based Libraries
To use the asyncio-based ones such as aiohttp, your app needs to be
compatible with asyncio's async/await programming model. The
SocketModeClient
only works with AsyncWebClient
and async listeners.
import asyncio
import os
from slack_sdk.web.async_client import AsyncWebClient
from slack_sdk.socket_mode.aiohttp import SocketModeClient
# Use async method
async def main():
from slack_sdk.socket_mode.response import SocketModeResponse
from slack_sdk.socket_mode.request import SocketModeRequest
# Initialize SocketModeClient with an app-level token + AsyncWebClient
client = SocketModeClient(
# This app-level token will be used only for establishing a connection
app_token=os.environ.get("SLACK_APP_TOKEN"), # xapp-A111-222-xyz
# You will be using this AsyncWebClient for performing Web API calls in listeners
web_client=AsyncWebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # xoxb-111-222-xyz
)
# Use async method
async def process(client: SocketModeClient, req: SocketModeRequest):
if req.type == "events_api":
# Acknowledge the request anyway
response = SocketModeResponse(envelope_id=req.envelope_id)
# Don't forget having await for method calls
await client.send_socket_mode_response(response)
# Add a reaction to the message if it's a new message
if req.payload["event"]["type"] == "message" \
and req.payload["event"].get("subtype") is None:
await client.web_client.reactions_add(
name="eyes",
channel=req.payload["event"]["channel"],
timestamp=req.payload["event"]["ts"],
)
if req.type == "interactive" \
and req.payload.get("type") == "shortcut":
if req.payload["callback_id"] == "hello-shortcut":
# Acknowledge the request
response = SocketModeResponse(envelope_id=req.envelope_id)
await client.send_socket_mode_response(response)
# Open a welcome modal
await client.web_client.views_open(
trigger_id=req.payload["trigger_id"],
view={
"type": "modal",
"callback_id": "hello-modal",
"title": {
"type": "plain_text",
"text": "Greetings!"
},
"submit": {
"type": "plain_text",
"text": "Good Bye"
},
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Hello!"
}
}
]
}
)
if req.type == "interactive" \
and req.payload.get("type") == "view_submission":
if req.payload["view"]["callback_id"] == "hello-modal":
# Acknowledge the request and close the modal
response = SocketModeResponse(envelope_id=req.envelope_id)
await client.send_socket_mode_response(response)
# Add a new listener to receive messages from Slack
# You can add more listeners like this
client.socket_mode_request_listeners.append(process)
# Establish a WebSocket connection to the Socket Mode servers
await client.connect()
# Just not to stop this process
await asyncio.sleep(float("inf"))
# You can go with other way to run it. This is just for easiness to try it out.
asyncio.run(main())