Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.
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
130 changes: 75 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,19 @@ First you'll create the basic elements of the app: A Slack client and webserver

```
from flask import Flask, request, make_response, Response
from slackclient import SlackClient
import os
import json
import ssl
from slack import WebClient

# Your app's Slack bot user token
SLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"]

# Verification token to verify received json payloads
SLACK_VERIFICATION_TOKEN = os.environ["SLACK_VERIFICATION_TOKEN"]

# Slack client for Web API requests
slack_client = SlackClient(SLACK_BOT_TOKEN)
slack_client = WebClient(token=SLACK_BOT_TOKEN, ssl=ssl_context)

# Flask webserver for incoming traffic from Slack
app = Flask(__name__)
Expand All @@ -80,19 +85,37 @@ def message_options():
# Parse the request payload
form_json = json.loads(request.form["payload"])

menu_options = {
"options": [
{
"text": "Chess",
"value": "chess"
},
{
"text": "Global Thermonuclear War",
"value": "war"
}
]
}
# Verify that the request came from Slack
verify_slack_token(form_json["token"])

# Dictionary of menu options which will be sent as JSON
menu_options = {
"options": [
{
"text": {
"type": "plain_text",
"text": "Cappuccino",
},
"value": "Cappuccino"
},
{
"text": {
"type": "plain_text",
"text": "Latte"
},
"value": "Latte"
},
{
"text": {
"type": "plain_text",
"text": "Cortado"
},
"value": "Cortado"
}
]
}

# Load options dict as JSON and respond to Slack
return Response(json.dumps(menu_options), mimetype='application/json')
```

Expand All @@ -106,22 +129,19 @@ def message_actions():
# Parse the request payload
form_json = json.loads(request.form["payload"])

# Check to see what the user's selection was and update the message
selection = form_json["actions"][0]["selected_options"][0]["value"]
# Verify that the request came from Slack
verify_slack_token(form_json["token"])

if selection == "war":
message_text = "The only winning move is not to play.\nHow about a nice game of chess?"
else:
message_text = ":horse:"
# Check to see what the user's selection was and update the message accordingly
selection = form_json["actions"][0]["selected_option"]["value"]

response = slack_client.api_call(
"chat.update",
response = slack_client.chat_postMessage(
channel=form_json["channel"]["id"],
ts=form_json["message_ts"],
text=message_text,
attachments=[]
ts=form_json["message"]["ts"],
text="One {}, right coming up! :coffee:".format(selection),
)

# Send an HTTP 200 response with empty body so Slack knows we're done here
return make_response("", 200)
```

Expand All @@ -131,45 +151,45 @@ Now that our endpoints are configured, we can build the message containing the m
In order to show the menu, we'll have to build the message attachment which will contain it.

```
message_attachments = [
{
"fallback": "Upgrade your Slack client to use messages like these.",
"color": "#3AA3E3",
"attachment_type": "default",
"callback_id": "menu_options_2319",
"actions": [
{
"name": "games_list",
"text": "Pick a game...",
"type": "select",
"data_source": "external"
attachments_json = [{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Would you like some coffee? :coffee:"
}
]
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Pick a beverage..."
},
"accessory": {
"type": "external_select",
"action_id": "menu_options_2319",
"placeholder": {
"type": "plain_text",
"text": "Select an item"
},
"min_query_length": 0
}
}
]
}]
```

Once the attachment JSON is ready, simply post a message to the channel, adding the attachment containing the menu.

```
slack_client.api_call(
"chat.postMessage",
channel="C09EM2073",
text="Shall we play a game?",
attachments=message_attachments
response = slack_client.chat_postMessage(
channel="#random",
attachments=json.dumps(attachments_json)
)
```

Take note of the ``"data_source": "external"`` attribute in the attachment JSON. This is how Slack knows to pull the menu options from the ``message_options`` endpoint we set up above.

```
slack_client.api_call(
"chat.postMessage",
channel="C09EM2073",
text="Shall we play a game?",
attachments=attachments_json
)
```
Take note of the ``"type": "external_select"`` attribute in the attachment JSON. This is how Slack knows to pull the menu options from the ``message_options`` endpoint we set up above.


Support
Expand Down
112 changes: 67 additions & 45 deletions example.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
from flask import Flask, request, make_response, Response
import os
import json

from slackclient import SlackClient
import ssl
from slack import WebClient

# Your app's Slack bot user token
SLACK_BOT_TOKEN = os.environ["SLACK_BOT_TOKEN"]

# Verification token to verify received json payloads
SLACK_VERIFICATION_TOKEN = os.environ["SLACK_VERIFICATION_TOKEN"]

ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE

# Slack client for Web API requests
slack_client = SlackClient(SLACK_BOT_TOKEN)
slack_client = WebClient(token=SLACK_BOT_TOKEN, ssl=ssl_context)

# Flask webserver for incoming traffic from Slack
app = Flask(__name__)
Expand All @@ -33,17 +39,30 @@ def message_options():

# Dictionary of menu options which will be sent as JSON
menu_options = {
"options": [
{
"text": "Cappuccino",
"value": "cappuccino"
},
{
"text": "Latte",
"value": "latte"
}
]
}
"options": [
{
"text": {
"type": "plain_text",
"text": "Cappuccino",
},
"value": "Cappuccino"
},
{
"text": {
"type": "plain_text",
"text": "Latte"
},
"value": "Latte"
},
{
"text": {
"type": "plain_text",
"text": "Cortado"
},
"value": "Cortado"
}
]
}

# Load options dict as JSON and respond to Slack
return Response(json.dumps(menu_options), mimetype='application/json')
Expand All @@ -60,50 +79,53 @@ def message_actions():
verify_slack_token(form_json["token"])

# Check to see what the user's selection was and update the message accordingly
selection = form_json["actions"][0]["selected_options"][0]["value"]
selection = form_json["actions"][0]["selected_option"]["value"]

if selection == "cappuccino":
message_text = "cappuccino"
else:
message_text = "latte"

response = slack_client.api_call(
"chat.update",
response = slack_client.chat_postMessage(
channel=form_json["channel"]["id"],
ts=form_json["message_ts"],
text="One {}, right coming up! :coffee:".format(message_text),
attachments=[] # empty `attachments` to clear the existing massage attachments
ts=form_json["message"]["ts"],
text="One {}, right coming up! :coffee:".format(selection),
)

# Send an HTTP 200 response with empty body so Slack knows we're done here
return make_response("", 200)


# Send a Slack message on load. This needs to be _before_ the Flask server is started

# A Dictionary of message attachment options
attachments_json = [
{
"fallback": "Upgrade your Slack client to use messages like these.",
"color": "#3AA3E3",
"attachment_type": "default",
"callback_id": "menu_options_2319",
"actions": [
{
"name": "bev_list",
"text": "Pick a beverage...",
"type": "select",
"data_source": "external"
attachments_json = [{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Would you like some coffee? :coffee:"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Pick a beverage..."
},
"accessory": {
"type": "external_select",
"action_id": "menu_options_2319",
"placeholder": {
"type": "plain_text",
"text": "Select an item"
},
"min_query_length": 0
}
]
}
]
}
]
}]

# Send a message with the above attachment, asking the user if they want coffee
slack_client.api_call(
"chat.postMessage",
channel="#python",
text="Would you like some coffee? :coffee:",
attachments=attachments_json
response = slack_client.chat_postMessage(
channel="#random",
attachments=json.dumps(attachments_json)
)

# Start the Flask server
Expand Down