Code Monkey home page Code Monkey logo

bolt-python's Introduction

Bolt Bolt logo for Python

Python Version pypi package Codecov

A Python framework to build Slack apps in a flash with the latest platform features. Read the getting started guide and look at our code examples to learn how to build apps using Bolt. The Python module documents are available here.

Setup

# Python 3.6+ required
python -m venv .venv
source .venv/bin/activate

pip install -U pip
pip install slack_bolt

Creating an app

Create a Bolt for Python app by calling a constructor, which is a top-level export. If you'd prefer, you can create an async app.

import logging
logging.basicConfig(level=logging.DEBUG)

from slack_bolt import App

# export SLACK_SIGNING_SECRET=***
# export SLACK_BOT_TOKEN=xoxb-***
app = App()

# Add functionality here

if __name__ == "__main__":
    app.start(3000)  # POST http://localhost:3000/slack/events

Running an app

export SLACK_SIGNING_SECRET=***
export SLACK_BOT_TOKEN=xoxb-***
python app.py

# in another terminal
ngrok http 3000

Running a Socket Mode app

If you use Socket Mode for running your app, SocketModeHandler is available for it.

import os
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

# Install the Slack app and get xoxb- token in advance
app = App(token=os.environ["SLACK_BOT_TOKEN"])

# Add functionality here

if __name__ == "__main__":
    # Create an app-level token with connections:write scope
    handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"])
    handler.start()

Run the app this way:

export SLACK_APP_TOKEN=xapp-***
export SLACK_BOT_TOKEN=xoxb-***
python app.py

# SLACK_SIGNING_SECRET is not required
# Running ngrok is not required

Listening for events

Apps typically react to a collection of incoming events, which can correspond to Events API events, actions, shortcuts, slash commands or options requests. For each type of request, there's a method to build a listener function.

# Listen for an event from the Events API
app.event(event_type)(fn)

# Convenience method to listen to only `message` events using a string or re.Pattern
app.message([pattern ,])(fn)

# Listen for an action from a Block Kit element (buttons, select menus, date pickers, etc)
app.action(action_id)(fn)

# Listen for dialog submissions
app.action({"callback_id": callbackId})(fn)

# Listen for a global or message shortcuts
app.shortcut(callback_id)(fn)

# Listen for slash commands
app.command(command_name)(fn)

# Listen for view_submission modal events
app.view(callback_id)(fn)

# Listen for options requests (from select menus with an external data source)
app.options(action_id)(fn)

The recommended way to use these methods are decorators:

@app.event(event_type)
def handle_event(event):
    pass

Making things happen

Most of the app's functionality will be inside listener functions (the fn parameters above). These functions are called with a set of arguments, each of which can be used in any order. If you'd like to access arguments off of a single object, you can use args, an slack_bolt.kwargs_injection.Args instance that contains all available arguments for that event.

Argument Description
body Dictionary that contains the entire body of the request (superset of payload). Some accessory data is only available outside of the payload (such as trigger_id and authorizations).
payload Contents of the incoming event. The payload structure depends on the listener. For example, for an Events API event, payload will be the event type structure. For a block action, it will be the action from within the actions list. The payload dictionary is also accessible via the alias corresponding to the listener (message, event, action, shortcut, view, command, or options). For example, if you were building a message() listener, you could use the payload and message arguments interchangably. An easy way to understand what's in a payload is to log it.
context Event context. This dictionary contains data about the event and app, such as the botId. Middleware can add additional context before the event is passed to listeners.
ack Function that must be called to acknowledge that your app received the incoming event. ack exists for all actions, shortcuts, view submissions, slash command and options requests. ack returns a promise that resolves when complete. Read more in Acknowledging events.
respond Utility function that responds to incoming events if it contains a response_url (shortcuts, actions, and slash commands).
say Utility function to send a message to the channel associated with the incoming event. This argument is only available when the listener is triggered for events that contain a channel_id (the most common being message events). say accepts simple strings (for plain-text messages) and dictionaries (for messages containing blocks).
client Web API client that uses the token associated with the event. For single-workspace installations, the token is provided to the constructor. For multi-workspace installations, the token is returned by using the OAuth library, or manually using the authorize function.
logger The built-in logging.Logger instance you can use in middleware/listeners.

Creating an async app

If you'd prefer to build your app with asyncio, you can import the AIOHTTP library and call the AsyncApp constructor. Within async apps, you can use the async/await pattern.

# Python 3.6+ required
python -m venv .venv
source .venv/bin/activate

pip install -U pip
# aiohttp is required
pip install slack_bolt aiohttp

In async apps, all middleware/listeners must be async functions. When calling utility methods (like ack and say) within these functions, it's required to use the await keyword.

# Import the async app instead of the regular one
from slack_bolt.async_app import AsyncApp

app = AsyncApp()

@app.event("app_mention")
async def event_test(body, say, logger):
    logger.info(body)
    await say("What's up?")

@app.command("/hello-bolt-python")
async def command(ack, body, respond):
    await ack()
    await respond(f"Hi <@{body['user_id']}>!")

if __name__ == "__main__":
    app.start(3000)

If you want to use another async Web framework (e.g., Sanic, FastAPI, Starlette), take a look at the built-in adapters and their examples.

Getting Help

The documentation has more information on basic and advanced concepts for Bolt for Python. Also, all the Python module documents of this library are available here.

If you otherwise get stuck, we're here to help. The following are the best ways to get assistance working through your issue:

  • Issue Tracker for questions, bug reports, feature requests, and general discussion related to Bolt for Python. Try searching for an existing issue before creating a new one.
  • Email our developer support team: [email protected]

bolt-python's People

Contributors

angrychimp avatar dependabot[bot] avatar filmaj avatar handyman5 avatar hirosassa avatar jlujan-invitae avatar johtani avatar korymath avatar misscoded avatar mwbrooks avatar naveensan1 avatar nickovs avatar ntarora avatar objectfox avatar pdontha avatar rhnsharma avatar sarayourfriend avatar seratch avatar shoryu-n avatar siegerts avatar smyja avatar srajiang avatar stevengill avatar tattee avatar torifukukaiou avatar vgnshiyer avatar vokey avatar williambergamin avatar wongjas avatar ysaxon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bolt-python's Issues

Lazy Listener Functions

This is a proposal for a new feature, "Lazy Listener Functions" in Bolt for Python. This is a new feature for advanced developers that want to build serious Slack apps using Function as a Service. Adding this feature doesn't prevent most developers from using Bolt as with Bolt JS and Bolt Java. If this feature is greatly successful for many users, we may implement the same concept in other Bolt frameworks in the future.

Motivation

The primary motivation of this feature is to realize a more handy way to run asynchronous code after acknowledging incoming requests from Slack within 3 seconds.

When I was thinking about possible solutions for FaaS support in Bolt for JS, I came up with a "two-phase app" concept.

However, I think it was not simple enough, and the concept was far different from the original Bolt framework.

Lazy listener functions are more powerful and simpler than that approach. This mechanism enables developers to more easily manage async operations, even in FaaS environments.

API Design

Let's say you have the following slash command listener. Of course, this works without any issues.

from slack_bolt import App
app = App()

command = "/start-long-task"

@app.command(command)
def do_everything_here(payload, ack, respond, client):
    if payload.get("text", None) is None:
        ack(f":x: Usage: {command} (task name here)")
    else:
        task_name = payload["text"]
        ack(f"Accepted! (task: {task_name})") # This immediately sends 200 OK to Slack

        # start a long task
        time.sleep(5) # Doing something meaningful takes time
        task_name = payload["text"]
        respond(f"Completed! (task: {task_name}, url: https://example.com/result)")

With Lazy listener functions, the same code can be as below. All you need to do is give two types of args, ack and lazy, to a listener. By using this interface, you can separate the acknowledgment phase and long operation in a concise way.

def respond_to_slack_within_3_seconds(payload, ack):
    if payload.get("text", None) is None:
        ack(f":x: Usage: {command} (task name here)")
    else:
        task_name = payload["text"]
        ack(f"Accepted! (task: {task_name})")

def run_backend_operation(respond, payload):
    time.sleep(5) # Doing something meaningful takes time
    task_name = payload["text"]
    respond(f"Completed! (task: {task_name}, url: https://example.com/result)")

app.command(command)(
    ack=respond_to_slack_within_3_seconds,
    lazy=[run_backend_operation]
)

ack is responsible for returning an immediate HTTP response to Slack API servers within 3 seconds. By contrast, lazy functions are not supposed to return any response. They can do anything by leveraging all the listener args apart from ack() utility. Also, they are completely free from 3-second timeouts.

As the lazy is a list, you can set multiple lazy functions to a single listener. The lazy functions will be executed in parallel.

def acknowledge_anyway(ack):
    ack()

def open_modal(payload, client):
    api_response = client.views_open(trigger_id=payload["trigger_id"], view={ })

def start_background_job(payload):
    # do a long task
    pass

app.command("/data-request")(
    ack=acknowledge_anyway,
    lazy=[open_modal, start_background_job],
)

Mechanism

For complex apps, this mechanism can improve code readability. But the biggest benefit of this API is not readability. This mechanism provides better flexibility for choosing the runtime to execute asynchronous operations.

To support Lazy Listener Functions, the App/AsyncApp class should have a LazyListenerRunner internally. The component manages and runs lazy functions. Its start method kicks an async operation with the indication of recursive execution. The run method runs the lazy function with given payload and respond and say utilities.

class LazyListenerRunner:
    # Bolt runs this method when starting a new async operation
    # This method copies the payload and sets the lazy function's information to it.
    # The duplicated request has lazy_only: True and lazy_function_name: str.
    def start(self, lazy_function: Callable[..., None], request: BoltRequest) -> None:
        pass

    # This method can be executed in any of a different thread, Future, and a different runtime
    # In the case of AWS Lambda, this method is supposed to be invoked in a different Lambda container.
    def run(self, lazy_function: Callable[..., None], request: BoltRequest) -> None:
        pass

LazyListenerRunner submits a new async execution to its runtime. The runtime to use is fully customizable. It can be a different thread, asyncio's Future, different server/container, and whatever you prefer.

The out-of-the-box AWS Lambda adapter takes advantage of it.

class LambdaLazyListenerRunner(LazyListenerRunner):
    def __init__(self):
        self.lambda_client = boto3.client("lambda")

    def start(self, function: Callable[..., None], request: BoltRequest) -> None:
        event: dict = request.context["lambda_request"] # duplicated original request
        headers = event["headers"]
        headers["x-slack-bolt-lazy-only"] = "1" # set lazy execution's specific header
        headers["x-slack-bolt-lazy-function-name"] = request.lazy_function_name # the function to run later
        event["method"] = "NONE"
        invocation = self.lambda_client.invoke(
            FunctionName=request.context["aws_lambda_function_name"],
            InvocationType="Event",
            Payload=json.dumps(event),
        )

Runners can rely on either of request.lazy_only / request.lazy_function_name (when sharing memory) or two x-slack-bolt- headers in recursive requests (when submitting a new request - these are converted to request.lazy_only and request.lazy_fucntion_name inside Bolt).

  • "x-slack-bolt-lazy-only" header value -> request.lazy_only: bool
  • "x-slack-bolt-lazy-function-name" header value -> request.lazy_function_name: Optional[str]

When Bolt runs App#dispatch (or AsyncApp#async_dispatch) method recursively, request.lazy_only should be True and request.lazy_function_name should exist. In the case, ack function and other lazy functions will be skipped.

Next Steps

I've already implemented the initial version of this feature. I will come up with a WIP (Work In Progress) pull request and am happy to hear feedback from other maintainers and the communities!

Requirements (place an x in each of the [ ])

  • I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue here.

Events API's auto_acknowledgement may occasionally fail

It can be easily reproduced by the code shared at #75 (comment)

Reproducible in:

The slack_bolt version

$ pip freeze | grep slack
-e [email protected]:slackapi/bolt-python.git@6355714bb6917f39cc2cee96f21463bc3eac1d74#egg=slack_bolt
slack-sdk==3.0.0a4

Python runtime version

(env_3.8.5) $ python --version
Python 3.8.5

OS info

(env_3.8.5) $ sw_vers && uname -v
ProductName:	Mac OS X
ProductVersion:	10.15.6
BuildVersion:	19G2021
Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64

Steps to reproduce:

    def test_stable_auto_ack(self):
        app = App(client=self.web_client, signing_secret=self.signing_secret)

        @app.event("reaction_added")
        def handle_app_mention():
            raise Exception("something wrong!")

        for _ in range(10):
            timestamp, body = (
                str(int(time())),
                json.dumps(self.valid_reaction_added_body),
            )
            request: BoltRequest = BoltRequest(
                body=body, headers=self.build_headers(timestamp, body)
            )
            response = app.dispatch(request)
            assert response.status == 200

Expected result:

Getting 200 OK all the times

Actual result:

Occasionally getting a 404 Not Found response

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Test failure due to Chalice's breaking change

$ travis_retry pytest tests/adapter_tests/
============================= test session starts ==============================
platform linux -- Python 3.6.7, pytest-5.4.3, py-1.7.0, pluggy-0.12.0
rootdir: /home/travis/build/slackapi/bolt-python, inifile: pytest.ini
plugins: cov-2.10.1
collected 53 items
tests/adapter_tests/test_aws_chalice.py ....F.                           [ 11%]
tests/adapter_tests/test_aws_lambda.py ........                          [ 26%]
tests/adapter_tests/test_bottle.py ...                                   [ 32%]
tests/adapter_tests/test_bottle_oauth.py .                               [ 33%]
tests/adapter_tests/test_cherrypy.py ....                                [ 41%]
tests/adapter_tests/test_cherrypy_oauth.py ..                            [ 45%]
tests/adapter_tests/test_django.py ....                                  [ 52%]
tests/adapter_tests/test_falcon.py ....                                  [ 60%]
tests/adapter_tests/test_fastapi.py ....                                 [ 67%]
tests/adapter_tests/test_flask.py ....                                   [ 75%]
tests/adapter_tests/test_lambda_s3_oauth_flow.py .                       [ 77%]
tests/adapter_tests/test_pyramid.py ....                                 [ 84%]
tests/adapter_tests/test_starlette.py ....                               [ 92%]
tests/adapter_tests/test_tornado.py ...                                  [ 98%]
tests/adapter_tests/test_tornado_oauth.py .                              [100%]
=================================== FAILURES ===================================
______________________ TestAwsChalice.test_lazy_listeners ______________________
self = <tests.adapter_tests.test_aws_chalice.TestAwsChalice object at 0x7fcdd5cbcfd0>
    def test_lazy_listeners(self):
        app = App(client=self.web_client, signing_secret=self.signing_secret,)
    
        def command_handler(ack):
            ack()
    
        def say_it(say):
            say("Done!")
    
        app.command("/hello-world")(ack=command_handler, lazy=[say_it])
    
        input = (
            "token=verification_token"
            "&team_id=T111"
            "&team_domain=test-domain"
            "&channel_id=C111"
            "&channel_name=random"
            "&user_id=W111"
            "&user_name=primary-owner"
            "&command=%2Fhello-world"
            "&text=Hi"
            "&enterprise_id=E111"
            "&enterprise_name=Org+Name"
            "&response_url=https%3A%2F%2Fhooks.slack.com%2Fcommands%2FT111%2F111%2Fxxxxx"
            "&trigger_id=111.111.xxx"
        )
        timestamp, body = str(int(time())), input
    
        chalice_app = Chalice(app_name="bolt-python-chalice")
        slack_handler = ChaliceSlackRequestHandler(app=app, chalice=chalice_app)
    
        headers = self.build_headers(timestamp, body)
        headers["x-slack-bolt-lazy-only"] = "1"
        headers["x-slack-bolt-lazy-function-name"] = "say_it"
    
        request: Request = Request(
            method="NONE",
            query_params={},
            uri_params={},
            context={},
            stage_vars=None,
            is_base64_encoded=False,
            body=body,
>           headers=headers,
        )
E       TypeError: __init__() got an unexpected keyword argument 'method'
tests/adapter_tests/test_aws_chalice.py:242: TypeError
=============================== warnings summary ===============================

Category (place an x in each of the [ ])

tests

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

say() doesn't work with some events (e.g., "reaction_added")

say() method may fail due to the proper extraction of channel ID inside Bolt for Python. One example we detected is "reaction_added" but we should check if there are any other patterns. We can find all the patterns that should be supported in Bolt for JS's test code.

Reproducible in:

The slack_bolt version

version 0.4.0a0

Python runtime version

any

OS info

any

Steps to reproduce:

@app.event("reaction_added")
def foo(say):
    say("foo")
DEBUG:slack_bolt.App:Checking listener: foo ...
DEBUG:slack_bolt.App:Running listener: foo ...
ERROR:slack_bolt.App:say without channel_id here is unsupported
Traceback (most recent call last):
  File "/path-to-project/app/app.py", line 375, in run_ack_function_asynchronously
    listener.run_ack_function(request=request, response=response)
  File "/path-to-project/listener/custom_listener.py", line 46, in run_ack_function
    return self.ack_function(
  File "samples/app.py", line 31, in foo
    say("foo")
  File "/path-to-project/context/say/say.py", line 52, in __call__
    raise ValueError("say without channel_id here is unsupported")
ValueError: say without channel_id here is unsupported

Expected result:

The app can post a message in the channel.

Actual result:

ValueError shared above.

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add samples running on Google App Engine

Although running Python apps on Google App Engine is pretty straight-forward, having a minimal working example under the samples directory may be helpful for beginners.

The page URLs

  • samples/google_app_engine

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Failed to find bot installation data

I'm using Bolt with Flask and I can't make it work. I'm using it with passenger and nginx. I've alread configured /slack/events but when I try to make the app react to app_home_opened I get this:

App 5912 output: [ pid=5912, time=2020-10-28 22:20:40,851 ]: Failed to find bot installation data for enterprise: none, team: T027KNX9B: [Errno 2] No such file or directory: './data/none-T027KNX9B/bot-latest'
App 5912 output: [ pid=5912, time=2020-10-28 22:20:40,851 ]: auth.test API call result is unexpectedly None

Reproducible in:

pip freeze | grep slack
python --version
sw_vers && uname -v # or `ver`

The slack_bolt version

slack-bolt==0.9.5b0
slack-sdk==3.0.0rc1

Python runtime version

Python 3.6.8

OS info

Ubuntu 18.04

Steps to reproduce:

App code:

import logging
import os
from slack_bolt import App
from slack_bolt.adapter.flask import SlackRequestHandler
from slack_bolt.oauth.oauth_settings import OAuthSettings
from slack_sdk.oauth.installation_store import FileInstallationStore
from slack_sdk.oauth.state_store import FileOAuthStateStore

logging.basicConfig(level=logging.DEBUG)

oauth_settings = OAuthSettings(
    client_id=os.environ["SLACK_CLIENT_ID"],
    client_secret=os.environ["SLACK_CLIENT_SECRET"],
    scopes=["channels:read", "groups:read", "chat:write"],
    redirect_uri=None,
    install_path="/slack/install",
    redirect_uri_path="/slack/oauth_redirect",
    state_store=FileOAuthStateStore(expiration_seconds=600, base_dir="./data")
)

app = App(
    signing_secret=os.environ["SLACK_SIGNING_SECRET"],
    installation_store=FileInstallationStore(base_dir="./data"),
    oauth_settings=oauth_settings
)

@app.middleware  # or app.use(log_request)
def log_request(logger, body, next):
    logger.debug(body)
    return next()


@app.event("app_mention")
def event_test(body, say, logger):
    logger.info(body)
    say("What's up?")


@app.event("message")
def handle_message():
    pass


from flask import Flask, request

BotApp = Flask(__name__)
handler = SlackRequestHandler(app)


@BotApp.route("/slack/events", methods=["POST"])
def slack_events():
    return handler.handle(request)


@BotApp.route("/slack/install", methods=["GET"])
def install():
    return handler.handle(request)


@BotApp.route("/slack/oauth_redirect", methods=["GET"])
def oauth_redirect():
    return handler.handle(request)


@app.event("app_home_opened")
def open_modal(client, event, logger):
  try:
    # view.publish is the method that your app uses to push a view to the Home tab
    client.views_publish(
      # the user that opened your app's app home
      user_id = event["user"],
      # the view object that appears in the app home
      view={
        "type": "home",
        "callback_id": "home_view",

        # body of the view
        "blocks": [
          {
            "type": "section",
            "text": {
              "type": "mrkdwn",
              "text": "*Welcome to your _App's Home_* :tada:"
            }
          },
          {
            "type": "divider"
          },
          {
            "type": "section",
            "text": {
              "type": "mrkdwn",
              "text": "This button won't do much for now but you can set up a listener for it using the `actions()` method and passing its unique `action_id`. See an example in the `examples` folder within your Bolt app."
            }
          },
          {
            "type": "actions",
            "elements": [
              {
                "type": "button",
                "text": {
                  "type": "plain_text",
                  "text": "Click me!"
                }
              }
            ]
          }
        ]
      }
    )
  except Exception as e:
    logger.error(f"Error opening modal: {e}")

if __name__ == "__main__":
	BotApp.run()

Expected result:

app reacted to the event

Actual result:

It fails, I think somehow it's not saving the OAuth data but I don't know why

Support app_uninstalled, tokens_revoked events

One of the built-in Global Middleware (MultiTeamsAuthorization) calls the authorize function for each request. This, expectedly fails for app_uninstalled and tokens_revoked events but they should still be allowed to be processed for clean-up purposes (removing rows from DB, etc)

Reproducible in:

The slack_bolt version

slack-bolt==0.9.3b0
slack-sdk==3.0.0b0

Python runtime version

Python 3.9.0

OS info

ProductName: Mac OS X
ProductVersion: 10.15.7
BuildVersion: 19H2
Darwin Kernel Version 19.6.0: Mon Aug 31 22:12:52 PDT 2020; root:xnu-6153.141.2~1/RELEASE_X86_64

Steps to reproduce:

(Share the commands to run, source code, and project settings (e.g., setup.py))

  1. Install an App to a workspace using OAuth (for multi-team authorization)
  2. Subscribe to app_uninstalled or tokens_revoked events
  3. Create function to listen for either of those events
  4. Uninstall the App from the Manage Apps page

Expected result:

The middleware should allow passing to the next middleware even if the AuthorizeResult is None so that cleanup can be done when a user uninstalls the app

Actual result:

INFO 2020-10-21 15:57:14,508 basehttp 15742 123145406881792 "POST /slack/events HTTP/1.1" 200 52
DEBUG 2020-10-21 15:59:50,150 app 15742 123145406881792 Applying slack_bolt.middleware.ssl_check.ssl_check.SslCheck
DEBUG 2020-10-21 15:59:50,150 app 15742 123145406881792 Applying slack_bolt.middleware.request_verification.request_verification.RequestVerification
DEBUG 2020-10-21 15:59:50,150 app 15742 123145406881792 Applying slack_bolt.middleware.authorization.multi_teams_authorization.MultiTeamsAuthorization
DEBUG 2020-10-21 15:59:50,173 app 15742 123145423671296 Applying slack_bolt.middleware.ssl_check.ssl_check.SslCheck
DEBUG 2020-10-21 15:59:50,173 app 15742 123145423671296 Applying slack_bolt.middleware.request_verification.request_verification.RequestVerification
DEBUG 2020-10-21 15:59:50,173 app 15742 123145423671296 Applying slack_bolt.middleware.authorization.multi_teams_authorization.MultiTeamsAuthorization
DEBUG 2020-10-21 15:59:50,546 authorize 15742 123145406881792 The stored bot token for enterprise_id: None team_id: TJ4GH3ZRC is no longer valid. (response: {'ok': False, 'error': 'account_inactive'})
ERROR 2020-10-21 15:59:50,546 multi_teams_authorization 15742 123145406881792 auth.test API call result is unexpectedly None
DEBUG 2020-10-21 15:59:50,549 authorize 15742 123145423671296 The stored bot token for enterprise_id: None team_id: TJ4GH3ZRC is no longer valid. (response: {'ok': False, 'error': 'account_inactive'})
ERROR 2020-10-21 15:59:50,555 multi_teams_authorization 15742 123145423671296 auth.test API call result is unexpectedly None

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Improve the compatibility with Bolt JS in OAuth flow support

We are not aiming to completely imitate Bolt for JS in this library as Python programming has different customs and idiomatic ways. That said, the current implementation may be confusing for developers who are already familiar with Bolt for JS.

The simplest configuration is to use env variables and the built-in file based implementations (FileInstallationStore, FileOAuthStateStore).

from slack_bolt import App
app = App() # use file based store implementations

if __name__ == "__main__":
    app.start(3000)

# pip install slack_bolt
# export SLACK_SIGNING_SECRET=***
# export SLACK_CLIENT_ID=111.111
# export SLACK_CLIENT_SECRET=***
# export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
# python app.py

This code internally initializes the following settings. The oauth_settings contains the options in InstallerOptions and InstallProviderOptions, and CallbackOptions in Bolt JS (and its underlying Node SDK). Some of the options are unsupported in Python yet but if many developers request to add them, we will add them in the future releases.

import os
from slack_bolt import App
from slack_bolt.oauth.oauth_settings import OAuthSettings
from slack_sdk.oauth.installation_store import FileInstallationStore
from slack_sdk.oauth.state_store import FileOAuthStateStore

app = App(
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
    installation_store=FileInstallationStore(),
    oauth_settings=OAuthSettings(
        client_id=os.environ.get("SLACK_CLIENT_ID"),
        client_secret=os.environ.get("SLACK_CLIENT_SECRET"),
        scopes=["app_mentions:read","channels:history","im:history","chat:write"],
        user_scopes=[],
        redirect_uri=None,
        install_path="/slack/install",
        redirect_uri_path="/slack/oauth_redirect",
        state_store=FileOAuthStateStore(expiration_seconds=600)
    )
)

if __name__ == "__main__":
    app.start(3000)

# pip install slack_bolt
# export SLACK_SIGNING_SECRET=***
# export SLACK_CLIENT_ID=111.111
# export SLACK_CLIENT_SECRET=***
# python app.py

As bolt-python v0.4 hasn't supported CallbackOptions equivalent options, we will add the ways to register the callback functions in a similar way.

from slack_bolt import BoltResponse
from slack_bolt.oauth.callback_options import CallbackOptions, SuccessArgs, FailureArgs

def success(args: SuccessArgs) -> BoltResponse:
    return BoltResponse(status=200, body="Thanks!")

def failure(args: FailureArgs) -> BoltResponse:
    return BoltResponse(status=args.suggested_status_code, body=args.reason)

app = App(
    signing_secret=os.environ.get("SLACK_SIGNING_SECRET"),
    oauth_settings=OAuthSettings(
        callback_options=CallbackOptions(
            success=success,
            failure=failure
        )
    ) # in this case, other options are loaded from env variables
)

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Solution for the use cases behind firewalls is needed

Problem

Inability to easily identifying requests coming from slack

Motivation

As slack is used in many work environments with strict security policies, it is a strong requisite to only expose the server/function to known hosts/ips.

Enhancement Proposal

It may result useful to provide an utility function or perhaps document how we can validate that the request comes indeed from slack. This would allow to open server's to certain IPs or lambdas to only trigger under certain conditions.
This would greatly reduce the attack surface on slack apps integrations.

Workaround

Currently, the promoted way of validating requests authenticity is by validating it against signing secret of the app. This is really useful, but it can't be used as a firewall rule, so it may be a prohibitive constraint for many possible integrations

Examples for sending messages outside of the response flow

There is some information that indicates to use the WebClient that is part of the Bolt app however under most contexts the WebClient has been initialized with an empty token (understandably a reasonable situation).

What is unknown is the appropriate method to acquire a given installation (the xoxb-) token as well as initialize either a new WebClient or use the client attached to the Bolt application.

The page URLs

Requirements

Adding additional examples for sending a message that is out of the normal response flow would be great. A simplistic example would be if a user makes a payment on a website, a message would be sent to a given installation and channel indicating the event happened.

Lazy listener functions on Google Cloud Functions

As of version 0.3.0a0, we don't support lazy listener functions on Google Cloud Functions (as I mentioned in #45). We may explore solutions for it if many developers request the feature.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Use "type": "block_actions" when "type" is absent and "action_id" exists in App.action listener constraints

Currently, some patterns supported by Bolt for JS are not yet available with Bolt for Python. For example, Bolt for Python requires "type": "block_actions" in the constraint object in an equivalent code to the following JS code. Also, it doesn't support filtering by a given block_id in the case yet.

https://github.com/slackapi/bolt-js/blob/0b37456f4781bf9262a36a6d200732840a156903/docs/_basic/listening_actions.md#listening-to-actions-using-a-constraint-object

// Your middleware will only be called when the action_id matches 'select_user' AND the block_id matches 'assign_ticket'
app.action({ action_id: 'select_user', block_id: 'assign_ticket' },
  async ({ action, ack, context }) => {
    await ack();
    try {
      const result = await app.client.reactions.add({
        token: context.botToken,
        name: 'white_check_mark',
        timestamp: action.ts,
        channel: action.channel.id
      });
    }
    catch (error) {
      console.error(error);
    }
  });

Reproducible in:

The slack_bolt version

v0.4.0a0

Python runtime version

any

OS info

any

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add reference page in documents

For developers that are not familiar with Bolt for JS, the kwargs injection in Bolt for Python may be a bit surprising. The document.

The interface is highly inspired by Bolt for JS and I agree this is not a common design in Python. The document should clearly mention this.

Considering the place to put this type of content, we may want to have FAQ section in the template. We can mention other common topics (e.g., how to use Bolt along with Flask, Django? how to install a valid version of Python runtime) there. Any thoughts? @shaydewael

The page URLs

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add authorize function support

The authorize function that is available in Bolt JS is not yet supported in Bolt for Python.

# these arguments should be injected in correspondence with their names
# also, the Bolt framework provides `args` as a single value as with middleware/listeners
def authorize(enterprise_id, team_id, user_id):
    return {
        "bot_id": "B111",
        "bot_user_id": "W111",
        "bot_token": "xoxb-"
    }

app = App(
    signing_secret: os.environ["SLACK_SIGNING_SECRET"],
    authorize: authorize
)

For most use cases, probably, developers prefer using installation_store. That being said if a developer would like to implement a simple but more flexible way to fetch installation data, going with the authorize function can be a handy way.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Others

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add middleware list for listener methods

Current:

    def shortcut(
        self,
        constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]],
        matchers: List[Callable[..., bool]] = []):
        def __call__(func):
            primary_matcher = builtin_matchers.shortcut(constraints)
            return self._register_listener(func, primary_matcher, matchers)

        return __call__

To-be:

    def shortcut(
        self,
        constraints: Union[str, Pattern, Dict[str, Union[str, Pattern]]],
        matchers: List[Callable[..., bool]] = [],
        middleware: List[Middleware] = []):
        def __call__(func):
            # TODO

        return __call__

event subscription url verification failed

When I add my ngrok url in api.slack.com for events subscription, the verification fails. Error:

DEBUG:slack_bolt.App:Applying slack_bolt.middleware.ssl_check.SslCheck
DEBUG:slack_bolt.App:Applying slack_bolt.middleware.request_verification.RequestVerification
DEBUG:slack_bolt.App:Applying slack_bolt.middleware.authorization.SingleTeamAuthorization
DEBUG:slack_bolt.App:Applying slack_bolt.middleware.ignoring_self_events.IgnoringSelfEvents
WARNING:slack_bolt.App:Unhandled request ({'token': '.....', 'challenge': '...', 'type': 'url_verification'})
127.0.0.1 - - [26/Jun/2020 12:55:41] "POST /slack/events HTTP/1.1" 404 -

Messages are received four times

Hello,

I have a similar error I had with the regular slack client : cf issue .

I switched to bolt in order to solve this issue and it was working for a couple of days but now I received the same message three to four times each time.

When I look at the logs, I have this for the same message:

{'client_msg_id': '59c755aa-80cf-4a77-8852-6073f776ca07', 'type': 'message', 'text': 'HPSM,.', 'user': 'U7PDK2W1J', 'ts': '1599757663.004700', 'team': 'T7P9RQX35', 'blocks': [{'type': 'rich_text', 'block_id': 'YhMu', 'elements': [{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'MESSAGE'}]}]}], 'channel': 'D8X4RT3PW', 'event_ts': '1599757663.004700', 'channel_type': 'im'}

{'client_msg_id': 'eece3ee7-2dab-4585-8bef-6be776645e80', 'type': 'message', 'text': 'HPSM,.', 'user': 'U7PDK2W1J', 'ts': '1599757626.004100', 'team': 'T7P9RQX35', 'blocks': [{'type': 'rich_text', 'block_id': 'oH4=p', 'elements': [{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'HPSM,.'}]}]}], 'channel': 'D8X4RT3PW', 'event_ts': '1599757626.004100', 'channel_type': 'im'}

(etc.)

And when I look at the IP sent, there are different.

Thank you in advance.

Add aliases for payload to align with Bolt for JS

Currently, Bolt for Python doesn't provide the following keyword arguments. This project should provide these keys to be compatible with Bolt for JS.

$ grep " this\['payload'\]" -R src/
src//types/options/index.ts:  body: this['payload'];
src//types/options/index.ts:  options: this['payload'];
src//types/shortcuts/index.ts:  shortcut: this['payload'];
src//types/shortcuts/index.ts:  body: this['payload'];
src//types/actions/index.ts:  action: this['payload'];
src//types/view/index.ts:  view: this['payload'];
src//types/command/index.ts:  command: this['payload'];
src//types/command/index.ts:  body: this['payload'];
src//types/events/index.ts:  event: this['payload'];
src//types/events/index.ts:  message: EventType extends 'message' ? this['payload'] : never;

https://github.com/slackapi/bolt-js/search?q=%22this%5B%27payload%27%5D%3B%22&unscoped_q=%22this%5B%27payload%27%5D%3B%22

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add global error handler

Description

As with other Bolt frameworks, the error handler should be customizable.

# TODO: error handler
self._framework_logger.exception(
f"Failed to run listener function (error: {e})"
)

# TODO: error handler
self._framework_logger.exception(
f"Failed to run listener function (error: {e})"
)

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements (place an x in each of the [ ])

  • [x I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue here.

IgnoringSelfEvents filters member_joined_channel/member_left_channel events out

The global middleware IgnoringSelfEvents is enabled by default for instances of App.

The docs show an example for Listener middleware on how to filter out messages with bot_message subtype. Since a global middleware is already enabled for this, there doesn't appear to be a way to purposefully react to bot events.

Looking through the code, I don't see any reason that this middleware should be required so it shouldn't break things to turn it off but I am not sure how to do that.

The use case is for member_joined_channel events. The app I am working on lets users pick a channel for the bot to work in and then, as expected, the bot is unable to post to that channel until it is invited so I need to monitor for the bot being invited to the channel.

I configured a simple function (@app.event("member_left_channel")) to watch for these events but the aforementioned middleware prevents them from being executed.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Others

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

"step" arg in listeners for workflow_step_execute events

My implementation was incomplete.

Reproducible in:

The slack_bolt version

The latest version still has this issue.

 $ pip freeze | grep slack
-e [email protected]:slackapi/bolt-python.git@6a8d38da5704be7d0d7124162057b3d71a77ed15#egg=slack_bolt
slack-sdk==3.0.0a9

Python runtime version

Any

OS info

Any

Steps to reproduce:

def execute(step, complete, fail):
    inputs = step["inputs"] # raises an Exception as the `step` is None

ws = WorkflowStep(
    callback_id="add_task",
    edit=edit,
    save=save,
    execute=execute,
)
app.step(ws)

Expected result:

step in workflow_step_execute listeners is a valid dict value.

Actual result:

step in workflow_step_execute listeners is None.

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add app_id in context objects

As discussed at #126, we can consider providing an easier way to access app_id (a.k.a. api_app_id in some payloads) in context objects (BoltContext in Bolt for Python).

This needs to be implemented in Bolt for JS and Bolt for Java as well.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Others

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Add samples for Google Cloud Functions users

Description

As we already have samples for AWS Lambda users, we can put some code for Google Cloud Functions under samples.

References:

The page URLs

  • ./samples in this repository

Requirements (place an x in each of the [ ])

  • I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue here.

payload != body in middleware/listener args

Currently, payload in middleware/listener args is an alias to body. This is not compatible with Bolt JS.

Reproducible in:

@app.action("action-id")
def handle_action(body, payload, action, ack):
    assert body != payload
    assert payload == action
    ack()

The slack_bolt version

The latest revision

Python runtime version

Any

OS info

Any

Steps to reproduce:

Run the above snippet in your Bolt app.

Expected result:

200 OK

Actual result:

500 Internal Server Error

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Async support

  • App(async=True)
  • Samples with the following frameworks:
    • aiohttp
    • Tornado
    • FastAPI (async)
    • Sanic
    • Vibora

Higher test coverage (particularly for adapters)

Currently, the test coverage of this project is around 78%. Before releasing the first-ever beta version, the percentage should be higher than 80%. The major reason for the law coverage is slack_bolt.adapter modules. We can add more tests for them.

https://codecov.io/gh/slackapi/bolt-python/tree/main/slack_bolt

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Migrate Docs off jekyll to sphinx?

Hi, let me first thank for an amazing port of slack bolt JS to python!

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter
  • Others

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Problem Description

There are so many documentation engines such as Jekyll, MKdocs but Sphinx prevails among all of them mainly for the readability of the result and plugins (I know others have plugins too but are nowhere near as mature as Sphinx).

I really like the colour scheme picked for the docs it reads very well with my dyslexia, however there are few issues I've ran into during reading.

  1. I've got lost in navigation during reading thinking that I'm still in section Basic Concepts and that the links below should point to the separate page not just anchor. It would be nice if the sub-sections highlighting reflected the position on the page if the intention is to provide SPA feel.

image

  1. This is where Sphinx shines, there are a lot of mentions about using middleware like ack, say, respond... etc but I had to reach out to the reading the code to actually find out which middleware a is available when.

Sphinx with apidoc is able to generate API documentation of the module which makes a navigation and learning the module much easier combined with the page search function makes the module learning curve much friendlier.

  1. On top of it Sphinx supports current MarkDown and also my beloved rst.

Let me know, what you think about it.

Jan

Add context.matches support in app.message handlers

Description

As with Bolt for jS, the app.message handler should provide context.matches to listener functions.

app.message(/^(hi|hello|hey).*/, async ({ context, say }) => {
  // RegExp matches are inside of context.matches
  const greeting = context.matches[0];

  await say(`${greeting}, how are you?`);
});

https://github.com/slackapi/bolt-js/blob/%40slack/bolt%402.2.3/docs/_basic/listening_messages.md#using-a-regexp-pattern

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements (place an x in each of the [ ])

  • I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue here.

SlackRequestHandler does not work on Lambda

Problem

I was create app by bolt for python, And deploy on Lambda.
But It's not work correct.

I investigated this issue and found problems in determining the following areas.
https://github.com/slackapi/bolt-python/blob/main/slack_bolt/adapter/aws_lambda/handler.py
line:35
method = event.get("requestContext", {}).get("http", {}).get("method")

On my Lambda environment, it's always return None.

Workaround

I modified it below.
method = event.get("requestContext", {}).get("httpMethod", {})

It's work correct.

top-level constructor arg "authorize" is not respected when having "oauth_flow" together

(Filling out the following details about bugs will help us solve your issue sooner.)

Reproducible in:

The slack_bolt version

0.9.4b0

Python runtime version

any

OS info

any

Steps to reproduce:

app = App(
    oauth_flow=oauth_flow,
    authorize=my_authorize_func # this won't be respected
)

Expected result:

The argument is used or the framework should tell something if it has to ignore it.

Actual result:

The argument is not used.

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

TypeError: __call__() got an unexpected keyword argument 'ack' in samples/aws_chalice/simple_app.py

Bug Report

I got an error "TypeError: __call__() got an unexpected keyword argument 'ack'" when I deployed samples/aws_chalice/simple_app.py to Chalice.

Reproducible in:

The slack_bolt version

slack-bolt==0.2.1a0
slack-sdk==3.0.0a3

Python runtime version

Python 3.7.7

OS info

ProductName:	Mac OS X
ProductVersion:	10.15.6
BuildVersion:	19G73
Darwin Kernel Version 19.6.0: Sun Jul  5 00:43:10 PDT 2020; root:xnu-6153.141.1~9/RELEASE_X86_64

Steps to reproduce:

(Share the commands to run, source code, and project settings (e.g., setup.py))

cd samples/aws_chalice
cp -p .chalice/config.json.simple .chalice/config.json
# edit .chalice/config.json to change environment variables.
cp -p app.py app.py.org
cp -p simple_app.py app.py
sh deploy.sh

Expected result:

deploy.sh should finish successfully.

If I commented out these 3 lines it finished without error.

# bolt_app.command("/hello-bolt-python-chalice")(
#     ack=respond_to_slack_within_3_seconds, subsequent=[can_be_long],
# )

Actual result:

Note: I copied simple_app.py to app.py.

Traceback (most recent call last):
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/cli/__init__.py", line 636, in main
    return cli(obj={})
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/click/core.py", line 829, in __call__
    return self.main(*args, **kwargs)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/cli/__init__.py", line 206, in deploy
    deployed_values = d.deploy(config, chalice_stage_name=stage)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/deploy/deployer.py", line 354, in deploy
    return self._deploy(config, chalice_stage_name)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/deploy/deployer.py", line 360, in _deploy
    self._validate_config(config)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/deploy/deployer.py", line 383, in _validate_config
    validate_configuration(config)
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/deploy/validate.py", line 40, in validate_configuration
    routes = config.chalice_app.routes
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/config.py", line 141, in chalice_app
    app = v()
  File "/Users/ota/Documents/python/bolt-python/venv/lib/python3.7/site-packages/chalice/cli/factory.py", line 275, in load_chalice_app
    app = importlib.import_module('app')
  File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/Users/ota/Documents/python/bolt-python/samples/aws_chalice/app.py", line 39, in <module>
    ack=respond_to_slack_within_3_seconds, subsequent=[can_be_long],
TypeError: __call__() got an unexpected keyword argument 'ack'

Requirements (place an x in each of the [ ])

  • This is a bug specific to this SDK project.
  • I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • I've read and agree to the Code of Conduct.
  • I've searched for any related issues and avoided creating a duplicate issue here.

app.action without type should work for more actions

In addition to #65, app.action should handle callback_id for dialog submissions. This is required for the compatibility with Bolt for JS.

#78 (comment)

Bolt for Python doesn't work with code for dialog submission yet (it works when passing a dict to app.action or use app.dialog_submission). I will create a ticket and will fix this.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

setup steps failing for me

Running the following setup steps fails on the last step for me.

python -m venv env
source env/bin/activate
pip install -U pip
pip install -U slackclient
pip install -U -i https://test.pypi.org/simple/ slack_bolt

failure:

pip install -U -i https://test.pypi.org/simple/ slack_bolt
Looking in indexes: https://test.pypi.org/simple/
Collecting slack_bolt
  Downloading https://test-files.pythonhosted.org/packages/b4/19/fd801d28cd2271098f0954cbc6b00199a1803bb4e56dfa4c694f941676e4/slack_bolt-0.1.0a1-py3-none-any.whl (28 kB)
ERROR: Could not find a version that satisfies the requirement slackclient==2.7.1 (from slack_bolt) (from versions: 1.0.1, 1.0.2, 1.0.3, 1.0.4, 1.0.6, 1.1.0, 1.1.1, 1.1.2, 1.1.3, 1.3.0, 1.3.1, 1.3.2, 2.0.0b1, 2.0.1)
ERROR: No matching distribution found for slackclient==2.7.1 (from slack_bolt)

Looks like i already have the latest slackclient

Requirement already up-to-date: slackclient in ./env/lib/python3.7/site-packages (2.7.2)

It is because of this line in setup.py

install_requires=[
        "slackclient==2.7.1", # TODO: will be replaced with slack_sdk==3.0.0
    ],

I'll send a fix in a sec here

Add more tests and samples for dialogs

Currently, the ack(errors=errors) implementation only assumes view submission requests. I will add samples and tests to verify if it works with dialogs.

#78 (comment)

ack(errors=errors) should work for dialogs too. If it doesn't work for dialog, I will fix it.

Category (place an x in each of the [ ])

  • slack_bolt.App and/or its core components
  • slack_bolt.async_app.AsyncApp and/or its core components
  • Adapters in slack_bolt.adapter

Requirements

Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.