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']
    )