slackapi / bolt-python Goto Github PK
View Code? Open in Web Editor NEWA framework to build Slack apps using Python
Home Page: https://slack.dev/bolt-python/
License: MIT License
A framework to build Slack apps using Python
Home Page: https://slack.dev/bolt-python/
License: MIT License
App(async=True)
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?`);
});
x
in each of the [ ]
)x
in each of the [ ]
)We should enable https://codecov.io/ integration to track test coverage.
x
in each of the [ ]
)I got an error "TypeError: __call__() got an unexpected keyword argument 'ack'"
when I deployed samples/aws_chalice/simple_app.py to Chalice.
slack_bolt
versionslack-bolt==0.2.1a0
slack-sdk==3.0.0a3
Python 3.7.7
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
(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
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],
# )
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'
x
in each of the [ ]
)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.
// 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);
}
});
slack_bolt
versionv0.4.0a0
any
any
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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 -
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
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
Currently, payload
in middleware/listener args is an alias to body
. This is not compatible with Bolt JS.
@app.action("action-id")
def handle_action(body, payload, action, ack):
assert body != payload
assert payload == action
ack()
slack_bolt
versionThe latest revision
Any
Any
Run the above snippet in your Bolt app.
200 OK
500 Internal Server Error
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
To be compatible with Bolt for JS
I got feedback that "I thought this SDK lacks asyncio support". This is not true! We should add documents for asyncio users by the beta announcement.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
Hi, let me first thank for an amazing port of slack bolt JS to python!
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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.
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.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.
rst
.Let me know, what you think about it.
Jan
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.
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
As with other Bolt frameworks, we'll be providing Japanese translation of the documents. @seratch will be working on this.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
Inability to easily identifying requests coming from slack
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.
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.
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
Add a Getting Started guide similar to Getting started with Bolt for JavaScript.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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.
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.
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],
)
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).
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.
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!
x
in each of the [ ]
)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.
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.
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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__
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.
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
A module that is equivalent to https://github.com/slackapi/node-slack-sdk/tree/master/packages/oauth
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;
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
We need to add additional information to the User-Agent header as with other Bolt frameworks.
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.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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)
slack_bolt
versionslack-bolt==0.9.3b0
slack-sdk==3.0.0b0
Python 3.9.0
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
(Share the commands to run, source code, and project settings (e.g., setup.py))
app_uninstalled
or tokens_revoked
eventsThe 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
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
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
In addition to #65, app.action
should handle callback_id for dialog submissions. This is required for the compatibility with Bolt for JS.
Bolt for Python doesn't work with code for dialog submission yet (it works when passing a dict to
app.action
or useapp.dialog_submission
). I will create a ticket and will fix this.
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
My implementation was incomplete.
slack_bolt
versionThe 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
Any
Any
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)
step
in workflow_step_execute
listeners is a valid dict value.
step
in workflow_step_execute
listeners is None
.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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
.
I modified it below.
method = event.get("requestContext", {}).get("httpMethod", {})
It's work correct.
Currently, the ack(errors=errors)
implementation only assumes view submission requests. I will add samples and tests to verify if it works with dialogs.
ack(errors=errors)
should work for dialogs too. If it doesn't work for dialog, I will fix it.
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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
pip freeze | grep slack
python --version
sw_vers && uname -v # or `ver`
slack_bolt
versionslack-bolt==0.9.5b0
slack-sdk==3.0.0rc1
Python 3.6.8
Ubuntu 18.04
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()
app reacted to the event
It fails, I think somehow it's not saving the OAuth data but I don't know why
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
)
x
in each of the [ ]
)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()
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.
slack_bolt
versionversion 0.4.0a0
any
any
@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
The app can post a message in the channel.
ValueError
shared above.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
As with other Bolt frameworks, the error handler should be customizable.
bolt-python/slack_bolt/app/app.py
Lines 321 to 324 in 3eea0c4
bolt-python/slack_bolt/app/async_app.py
Lines 352 to 355 in 3eea0c4
x
in each of the [ ]
)slack_bolt.App
and/or its core componentsslack_bolt.async_app.AsyncApp
and/or its core componentsslack_bolt.adapter
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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.
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.
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.
x
in each of the [ ]
)Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
(Filling out the following details about bugs will help us solve your issue sooner.)
slack_bolt
version0.9.4b0
any
any
app = App(
oauth_flow=oauth_flow,
authorize=my_authorize_func # this won't be respected
)
The argument is used or the framework should tell something if it has to ignore it.
The argument is not used.
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
As we already have samples for AWS Lambda users, we can put some code for Google Cloud Functions under samples
.
References:
./samples
in this repositoryx
in each of the [ ]
)$ 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 ===============================
x
in each of the [ ]
)tests
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
It can be easily reproduced by the code shared at #75 (comment)
slack_bolt
version$ pip freeze | grep slack
-e [email protected]:slackapi/bolt-python.git@6355714bb6917f39cc2cee96f21463bc3eac1d74#egg=slack_bolt
slack-sdk==3.0.0a4
(env_3.8.5) $ python --version
Python 3.8.5
(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
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
Getting 200 OK all the times
Occasionally getting a 404 Not Found response
Please read the Contributing guidelines and Code of Conduct before creating this issue or pull request. By submitting, you are agreeing to those rules.
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.