Examples¶
As you navigate through Slack Api Docs. you may find yourself with 20+ tabs open and still no clue on how to accomplish what you want.
If that’s your case, this section might help you getting started quickly
There are examples of the most frequent use cases. Ranging from a bot with just a few commands, to a full workflow of registration and callbacks on userinput.
Examples:
Commands¶
from flask import Flask
from slackify import Slackify, request, reply_text
# Important! Before running set FLASK_APP=examples.async_task:app
app = Flask(__name__)
slackify = Slackify(app=app)
@slackify.command
def hello():
form = request.form['command']
text = request.form['text']
return reply_text(f'You called `{form} {text}`')
@slackify.command(name='bye')
def goodbye():
return reply_text('Goodbye')
Interactive Actions¶
from flask import Flask
from slackify import Slackify, respond, Slack, reply, request, OK
import json
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack('xoxb-SECRET-token')
def text_block(text):
return {
"type": "section",
"text": {
"type": "mrkdwn",
"text": text
}
}
@slackify.command
def askme():
"""Slack API recommended way of making messages interactive is through blocks
Messages no longer are simple text. Even if you want to send just text, the
recommended way is to create a section block, with the text property.
"""
# This is simple text, wrapped in a section to comply with slack new recommendations
message_block = {
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Do you like bots?"
}
}
# And here it gets cool. Actions blocks are what make messages special.
# Here we include two button elements. But there are many more elements.
# Check them out at https://api.slack.com/reference/block-kit/block-elements
buttons_block = {
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "Yes"
},
"style": "primary",
"value": "i_like_bots",
"action_id": "yes"
},
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "No"
},
"style": "danger",
"value": "i_dont_like_bots",
"action_id": "no"
}
]
}
blocks = [
message_block,
buttons_block
]
message_as_blocks = {
'blocks': blocks
}
return reply(message_as_blocks)
@slackify.action("yes")
def yes():
"""You may ask here, why do we respond to response_url instead of the request itself?
Well, slack decided you can't respond directly to interaction actions requests.
So we must use the response_url. If you know why did they decide this please tell
me. I'm sure they might be a reason but it isn't obvious to me..
"""
action = json.loads(request.form["payload"])
text_blok = text_block('Super! I do too :thumbsup:')
blocks = {'blocks': [text_blok]}
respond(action['response_url'], blocks)
return OK
@slackify.action("no")
def no():
action = json.loads(request.form["payload"])
text_blok = text_block('Boo! You are so boring :thumbsdown:')
blocks = {'blocks': [text_blok]}
respond(action['response_url'], blocks)
return OK
Modals (Views)¶
from flask import Flask
from slackify import Slackify, request, text_block, Slack, ACK
import json
from slackify.tasks import async_task
# Important! Before running set FLASK_APP=examples.async_task:app
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack('xoxb-SECRET-TOKEN')
@slackify.command
def register():
username_input_block = {
"type": "input",
"block_id": "username_block",
"element": {
"type": "plain_text_input",
"placeholder": {
"type": "plain_text",
"text": "Enter your username"
},
"action_id": "username_value"
},
"label": {
"type": "plain_text",
"text": "👤 Username",
"emoji": True
}
}
password_input_block = {
"type": "input",
"block_id": "password_block",
"element": {
"type": "plain_text_input",
"placeholder": {
"type": "plain_text",
"text": "Enter your password"
},
"action_id": "password_value"
},
"label": {
"type": "plain_text",
"text": "🔑 Password",
"emoji": True
}
}
modal_blocks = [
username_input_block,
password_input_block,
]
registration_form = {
"type": "modal",
"callback_id": "registration_form",
"title": {
"type": "plain_text",
"text": "My First Modal",
"emoji": True
},
"submit": {
"type": "plain_text",
"text": "Register",
"emoji": True
},
"close": {
"type": "plain_text",
"text": "Cancel",
"emoji": True
},
"blocks": modal_blocks
}
cli.views_open(
trigger_id=request.form['trigger_id'],
view=registration_form
)
return ACK
@slackify.view("registration_form")
def register_callback():
"""You may ask here, why DON'T we also use the response_url as we did in actions.py?
Well, slack doesn't include a response_url on view_submission payload.
https://api.slack.com/surfaces/modals/using#handling_submissions#modal_response_url
From that link we understand we have 3 options:
- Use the WebClient API
- Use a webhook,
- Include a `conversation_select` block in your modal view.
We'll stick to the first one as it is the easiest for demostration purposes. But
read the link to respond by any of the other options.
"""
action = json.loads(request.form["payload"])
response = action['view']['state']['values']
text_blok = text_block(f':heavy_check_mark: You are now registered.\nPayload:\n```{response}```')
send_message(cli, [text_blok], action['user']['id'])
return ACK
@async_task
def send_message(cli, blocks, user_id):
return cli.chat_postMessage(channel=user_id, user_id=user_id, blocks=blocks)
Shortcuts¶
from flask import Flask
from slackify import Slackify, request, Slack
import json
# Important! Before running set FLASK_APP=examples.async_task:app
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack('xoxb-SECRET-bot-token')
@slackify.shortcut('funny_joke')
def tell_joke():
payload = json.loads(request.form['payload'])
user = payload['user']
name = user.get('username', user.get('id'))
cli.chat_postMessage(channel='#general', text=f'Knock Knock `{name}`')
return '', 200
Event Hooks¶
import os
from flask import Flask
from slackify import Slackify, Slack
# Important! Before running set FLASK_APP=examples.async_task:app
app = Flask(__name__)
slackify = Slackify(app=app)
slack = Slack(os.environ["SLACK_BOT_TOKEN"])
@slackify.event("message")
def handle_message(payload):
"""Listen to messages containing `python` and react with python emoji
Note:
Your event handler function *MUST* accept a positional argument with the event payload
Preconditions:
- Setup event subscription https://api.slack.com/events-api and point to `/slack/events` uri
- Suscribe to `message.channels` events so your app gets notified of this event
"""
event = payload["event"]
if event.get("subtype") is None and 'python' in event.get('text', ''):
slack.reactions_add(
name='snake',
channel=event["channel"],
timestamp=event['ts']
)
Message Hooks¶
import re
import os
from flask import Flask
from slackify import Slackify, Slack
# Important! Before running set FLASK_APP=examples.async_task:app
app = Flask(__name__)
slackify = Slackify(app=app)
slack = Slack(os.getenv('BOT_TOKEN'))
@slackify.message('hello')
def say_hi(payload):
event = payload['event']
slack.chat_postMessage(channel=event['channel'], text='Hi! 👋')
BYE_REGEX = re.compile(r'bye|goodbye|see you|chau')
@slackify.message(BYE_REGEX)
def say_bye(payload):
event = payload['event']
slack.chat_postMessage(
channel=event['channel'],
text=f"See you tomorrow <@{event['user']}> 👋"
)
Home Tab¶
import os
from flask import Flask
from slackify import Slackify, request, reply_text, Slack, ACK
# Initialize App
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack(os.getenv('SLACK_BOT_TOKEN'))
@slackify.command
def update_home_view():
user = request.form['user_id']
sample = {
"type": "home",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "A simple stack of blocks for the simple sample Block Kit Home tab."
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"action_id": "tell_joke",
"text": {
"type": "plain_text",
"text": "Tell me a Joke",
"emoji": True
}
},
]
}
]
}
resp = cli.views_publish(
user_id=user,
view=sample
)
assert resp.get('ok', False), f"Could not build home tab.\nError:\n{resp.data!r}"
return reply_text('Home tab updated.')
@slackify.action('tell_joke')
def tell_a_joke(action):
resp = cli.chat_postMessage(
channel=action['user']['id'], # Send the user a private message through slackbot
text="I can't remember any joke right now 😅"
)
assert resp.get('ok'), f"Error sending message. {resp.data}"
return ACK
if __name__ == '__main__':
app.run('0.0.0.0', port=3000)
Async tasks¶
import time
from flask import Flask
from slackify import Slackify, async_task, reply_text, Slack
# Important! Before running set FLASK_APP=examples.async_task:app
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack('xoxb-SECRET-token')
@slackify.command()
def hello():
my_background_job()
return reply_text('Instant Response')
@async_task
def my_background_job():
"""Non blocking long task"""
time.sleep(5) # Wait more than slack's 3 seconds time limit
cli.chat_postMessage(channel='#general', text='I come from the future')
return
As a Blueprint¶
# slack_blueprint.py
from slackify import Slackify, Blueprint, reply_text
bp = Blueprint('slackify_bp', __name__, url_prefix='/slack')
slackify = Slackify(app=bp)
@slackify.command
def hello():
return reply_text('Hello from a blueprint')
# app.py
from flask import Flask # noqa: Ignore E402, as it is an example
# from slack_blueprint import bp
def create_app():
app = Flask(__name__)
app.register_blueprint(bp)
return app
Arguments Injection¶
from flask import Flask
from slackify import Slackify, reply_text, Slack, ACK
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack('xoxb-SECRET')
@slackify.command
def hello(command, command_args, response_url):
return reply_text(f"You called `{command} {command_args}`. POST to {response_url} for delayed responses (>3sec)")
@slackify.shortcut('greet_me')
def goodbye(payload):
cli.chat_postMessage(channel='#general', text=f'Knock Knock\n`{payload}`')
return ACK
Block Messages¶
import os
from slackify import Slackify, Flask, Slack
# Initialize App
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack(os.getenv('SLACK_BOT_TOKEN'))
# Create greeting with formatted text response
@slackify.message('hello')
def say_hi(payload):
event = payload['event']
cli.chat_postMessage(
channel=event['channel'],
# Blocks can be used to insert markdown support for links, images, and more.
# Check out https://api.slack.com/reference/block-kit for more.
blocks=[
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Danny Torrence left the following review for your property:"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "<https://example.com|Overlook Hotel> \n :star:\n" + \
"Doors had too many axe holes, guest in room " + \
"237 was far too rowdy, whole place felt stuck in the 1920s."
},
"accessory": {
"type": "image",
"image_url": "https://images.pexels.com/photos/750319/pexels-photo-750319.jpeg",
"alt_text": "Haunted hotel image"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Average Rating*\n1.0"
}
]
}
]
)
Full example¶
import os
import random
import re
from flask import Flask
from slackify import (ACK, OK, Slackify, async_task, block_reply,
request, respond, text_block, Slack)
app = Flask(__name__)
slackify = Slackify(app=app)
cli = Slack(os.getenv('BOT_TOKEN'))
@slackify.command
def hello():
YES = 'yes'
NO = 'no'
yes_no_buttons_block = {
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "Yes"
},
"style": "primary",
"value": "i_like_bots",
"action_id": YES
},
{
"type": "button",
"text": {
"type": "plain_text",
"emoji": True,
"text": "No"
},
"style": "danger",
"value": "i_dont_like_bots",
"action_id": NO
}
]
}
blocks = [
text_block('Do you like Bots?'),
yes_no_buttons_block
]
return block_reply(blocks)
@slackify.action("yes")
def yes(action):
text_blok = text_block('Super! I do too :thumbsup:')
respond(action['response_url'], {'blocks': [text_blok]})
return OK
@slackify.action("no")
def no(action):
text_blok = text_block('Boo! You are so boring :thumbsdown:')
respond(action['response_url'], {'blocks': [text_blok]})
return OK
@slackify.command
def register():
username_input_block = {
"type": "input",
"block_id": "username_block",
"element": {
"type": "plain_text_input",
"placeholder": {
"type": "plain_text",
"text": "Enter your username"
},
"action_id": "username_value"
},
"label": {
"type": "plain_text",
"text": "👤 Username",
"emoji": True
}
}
password_input_block = {
"type": "input",
"block_id": "password_block",
"element": {
"type": "plain_text_input",
"placeholder": {
"type": "plain_text",
"text": "Enter your password"
},
"action_id": "password_value"
},
"label": {
"type": "plain_text",
"text": "🔑 Password",
"emoji": True
}
}
modal_blocks = [
username_input_block,
password_input_block,
]
callback_id = 'registration_form'
registration_form = {
"type": "modal",
"callback_id": callback_id,
"title": {
"type": "plain_text",
"text": "My First Modal",
"emoji": True
},
"submit": {
"type": "plain_text",
"text": "Register",
"emoji": True
},
"close": {
"type": "plain_text",
"text": "Cancel",
"emoji": True
},
"blocks": modal_blocks
}
cli.views_open(
trigger_id=request.form['trigger_id'],
view=registration_form
)
return OK
@slackify.view("registration_form")
def register_callback(payload):
response = payload['view']['state']['values']
text_blok = text_block(f':heavy_check_mark: You are now registered.\nForm payload:\n```{response}```')
send_message(cli, [text_blok], payload['user']['id'])
return ACK
@async_task
def send_message(cli, blocks, user_id):
return cli.chat_postMessage(channel=user_id, user_id=user_id, blocks=blocks)
@slackify.shortcut('dice_roll')
def dice_roll(payload):
dice_value = random.randint(1, 6)
msg = f'🎲 {dice_value}'
send_message(cli, blocks=[text_block(msg)], user_id=payload['user']['id'])
return ACK
@slackify.event('reaction_added')
def echo_reaction(payload):
event = payload['event']
reaction = event['reaction']
cli.reactions_add(
name=reaction,
channel=event['item']['channel'],
timestamp=event['item']['ts']
)
@slackify.message('hello')
def say_hi(payload):
event = payload['event']
cli.chat_postMessage(channel=event['channel'], text='Hi! 👋')
@slackify.message(re.compile(r'python', re.IGNORECASE))
def reply_python_emoji(payload):
event = payload['event']
cli.reactions_add(
name='snake',
channel=event['channel'],
timestamp=event['ts']
)