├── .flake8
├── .git-blame-ignore-revs
├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE
│ ├── 01_question.md
│ ├── 02_enhancement.md
│ ├── 03_document.md
│ ├── 04_bug.md
│ └── config.yml
├── contributing.md
├── dependabot.yml
├── issue_template.md
├── maintainers_guide.md
├── pull_request_template.md
└── workflows
│ ├── codecov.yml
│ ├── docs-deploy.yml
│ ├── flake8.yml
│ ├── mypy.yml
│ ├── tests.yml
│ └── triage-issues.yml
├── .gitignore
├── LICENSE
├── MANIFEST.in
├── README.md
├── codecov.yml
├── docs
├── .gitignore
├── README.md
├── babel.config.js
├── content
│ ├── concepts
│ │ ├── acknowledge.md
│ │ ├── actions.md
│ │ ├── adapters.md
│ │ ├── ai-apps.md
│ │ ├── app-home.md
│ │ ├── async.md
│ │ ├── authenticating-oauth.md
│ │ ├── authorization.md
│ │ ├── commands.md
│ │ ├── context.md
│ │ ├── custom-adapters.md
│ │ ├── custom-steps-dynamic-options.md
│ │ ├── custom-steps.md
│ │ ├── errors.md
│ │ ├── event-listening.md
│ │ ├── global-middleware.md
│ │ ├── lazy-listeners.md
│ │ ├── listener-middleware.md
│ │ ├── logging.md
│ │ ├── message-listening.md
│ │ ├── message-sending.md
│ │ ├── opening-modals.md
│ │ ├── select-menu-options.md
│ │ ├── shortcuts.md
│ │ ├── socket-mode.md
│ │ ├── steps-from-apps.md
│ │ ├── token-rotation.md
│ │ ├── updating-pushing-views.md
│ │ ├── view-submissions.md
│ │ └── web-api.md
│ ├── getting-started.md
│ ├── index.md
│ └── tutorial
│ │ ├── ai-chatbot.md
│ │ ├── custom-steps-for-jira.md
│ │ ├── custom-steps-workflow-builder-existing.md
│ │ ├── custom-steps-workflow-builder-new.md
│ │ ├── custom-steps.md
│ │ └── modals.md
├── docusaurus.config.js
├── footerConfig.js
├── i18n
│ └── ja-jp
│ │ ├── README.md
│ │ ├── code.json
│ │ ├── docusaurus-plugin-content-docs
│ │ ├── current.json
│ │ └── current
│ │ │ ├── concepts
│ │ │ ├── acknowledge.md
│ │ │ ├── actions.md
│ │ │ ├── adapters.md
│ │ │ ├── app-home.md
│ │ │ ├── assistant.md
│ │ │ ├── async.md
│ │ │ ├── authenticating-oauth.md
│ │ │ ├── authorization.md
│ │ │ ├── commands.md
│ │ │ ├── context.md
│ │ │ ├── custom-adapters.md
│ │ │ ├── custom-steps.md
│ │ │ ├── errors.md
│ │ │ ├── event-listening.md
│ │ │ ├── global-middleware.md
│ │ │ ├── lazy-listeners.md
│ │ │ ├── listener-middleware.md
│ │ │ ├── logging.md
│ │ │ ├── message-listening.md
│ │ │ ├── message-sending.md
│ │ │ ├── opening-modals.md
│ │ │ ├── select-menu-options.md
│ │ │ ├── shortcuts.md
│ │ │ ├── socket-mode.md
│ │ │ ├── token-rotation.md
│ │ │ ├── updating-pushing-views.md
│ │ │ ├── view-submissions.md
│ │ │ └── web-api.md
│ │ │ ├── getting-started.md
│ │ │ └── legacy
│ │ │ └── steps-from-apps.md
│ │ └── docusaurus-theme-classic
│ │ ├── footer.json
│ │ └── navbar.json
├── navbarConfig.js
├── package-lock.json
├── package.json
├── sidebars.js
├── src
│ ├── css
│ │ └── custom.css
│ └── theme
│ │ └── NotFound
│ │ ├── Content
│ │ └── index.js
│ │ └── index.js
└── static
│ ├── .nojekyll
│ ├── api-docs
│ └── slack_bolt
│ │ ├── adapter
│ │ ├── aiohttp
│ │ │ └── index.html
│ │ ├── asgi
│ │ │ ├── aiohttp
│ │ │ │ └── index.html
│ │ │ ├── async_handler.html
│ │ │ ├── base_handler.html
│ │ │ ├── builtin
│ │ │ │ └── index.html
│ │ │ ├── http_request.html
│ │ │ ├── http_response.html
│ │ │ ├── index.html
│ │ │ └── utils.html
│ │ ├── aws_lambda
│ │ │ ├── chalice_handler.html
│ │ │ ├── chalice_lazy_listener_runner.html
│ │ │ ├── handler.html
│ │ │ ├── index.html
│ │ │ ├── internals.html
│ │ │ ├── lambda_s3_oauth_flow.html
│ │ │ ├── lazy_listener_runner.html
│ │ │ └── local_lambda_client.html
│ │ ├── bottle
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── cherrypy
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── django
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── falcon
│ │ │ ├── async_resource.html
│ │ │ ├── index.html
│ │ │ └── resource.html
│ │ ├── fastapi
│ │ │ ├── async_handler.html
│ │ │ └── index.html
│ │ ├── flask
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── google_cloud_functions
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── pyramid
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── sanic
│ │ │ ├── async_handler.html
│ │ │ └── index.html
│ │ ├── socket_mode
│ │ │ ├── aiohttp
│ │ │ │ └── index.html
│ │ │ ├── async_base_handler.html
│ │ │ ├── async_handler.html
│ │ │ ├── async_internals.html
│ │ │ ├── base_handler.html
│ │ │ ├── builtin
│ │ │ │ └── index.html
│ │ │ ├── index.html
│ │ │ ├── internals.html
│ │ │ ├── websocket_client
│ │ │ │ └── index.html
│ │ │ └── websockets
│ │ │ │ └── index.html
│ │ ├── starlette
│ │ │ ├── async_handler.html
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ ├── tornado
│ │ │ ├── async_handler.html
│ │ │ ├── handler.html
│ │ │ └── index.html
│ │ └── wsgi
│ │ │ ├── handler.html
│ │ │ ├── http_request.html
│ │ │ ├── http_response.html
│ │ │ ├── index.html
│ │ │ └── internals.html
│ │ ├── app
│ │ ├── app.html
│ │ ├── async_app.html
│ │ ├── async_server.html
│ │ └── index.html
│ │ ├── async_app.html
│ │ ├── authorization
│ │ ├── async_authorize.html
│ │ ├── async_authorize_args.html
│ │ ├── authorize.html
│ │ ├── authorize_args.html
│ │ ├── authorize_result.html
│ │ └── index.html
│ │ ├── context
│ │ ├── ack
│ │ │ ├── ack.html
│ │ │ ├── async_ack.html
│ │ │ ├── index.html
│ │ │ └── internals.html
│ │ ├── assistant
│ │ │ ├── assistant_utilities.html
│ │ │ ├── async_assistant_utilities.html
│ │ │ ├── index.html
│ │ │ ├── internals.html
│ │ │ ├── thread_context
│ │ │ │ └── index.html
│ │ │ └── thread_context_store
│ │ │ │ ├── async_store.html
│ │ │ │ ├── default_async_store.html
│ │ │ │ ├── default_store.html
│ │ │ │ ├── file
│ │ │ │ └── index.html
│ │ │ │ ├── index.html
│ │ │ │ └── store.html
│ │ ├── async_context.html
│ │ ├── base_context.html
│ │ ├── complete
│ │ │ ├── async_complete.html
│ │ │ ├── complete.html
│ │ │ └── index.html
│ │ ├── context.html
│ │ ├── fail
│ │ │ ├── async_fail.html
│ │ │ ├── fail.html
│ │ │ └── index.html
│ │ ├── get_thread_context
│ │ │ ├── async_get_thread_context.html
│ │ │ ├── get_thread_context.html
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── respond
│ │ │ ├── async_respond.html
│ │ │ ├── index.html
│ │ │ ├── internals.html
│ │ │ └── respond.html
│ │ ├── save_thread_context
│ │ │ ├── async_save_thread_context.html
│ │ │ ├── index.html
│ │ │ └── save_thread_context.html
│ │ ├── say
│ │ │ ├── async_say.html
│ │ │ ├── index.html
│ │ │ ├── internals.html
│ │ │ └── say.html
│ │ ├── set_status
│ │ │ ├── async_set_status.html
│ │ │ ├── index.html
│ │ │ └── set_status.html
│ │ ├── set_suggested_prompts
│ │ │ ├── async_set_suggested_prompts.html
│ │ │ ├── index.html
│ │ │ └── set_suggested_prompts.html
│ │ └── set_title
│ │ │ ├── async_set_title.html
│ │ │ ├── index.html
│ │ │ └── set_title.html
│ │ ├── error
│ │ └── index.html
│ │ ├── index.html
│ │ ├── kwargs_injection
│ │ ├── args.html
│ │ ├── async_args.html
│ │ ├── async_utils.html
│ │ ├── index.html
│ │ └── utils.html
│ │ ├── lazy_listener
│ │ ├── async_internals.html
│ │ ├── async_runner.html
│ │ ├── asyncio_runner.html
│ │ ├── index.html
│ │ ├── internals.html
│ │ ├── runner.html
│ │ └── thread_runner.html
│ │ ├── listener
│ │ ├── async_builtins.html
│ │ ├── async_listener.html
│ │ ├── async_listener_completion_handler.html
│ │ ├── async_listener_error_handler.html
│ │ ├── async_listener_start_handler.html
│ │ ├── asyncio_runner.html
│ │ ├── builtins.html
│ │ ├── custom_listener.html
│ │ ├── index.html
│ │ ├── listener.html
│ │ ├── listener_completion_handler.html
│ │ ├── listener_error_handler.html
│ │ ├── listener_start_handler.html
│ │ └── thread_runner.html
│ │ ├── listener_matcher
│ │ ├── async_builtins.html
│ │ ├── async_listener_matcher.html
│ │ ├── builtins.html
│ │ ├── custom_listener_matcher.html
│ │ ├── index.html
│ │ └── listener_matcher.html
│ │ ├── logger
│ │ ├── index.html
│ │ └── messages.html
│ │ ├── middleware
│ │ ├── assistant
│ │ │ ├── assistant.html
│ │ │ ├── async_assistant.html
│ │ │ └── index.html
│ │ ├── async_builtins.html
│ │ ├── async_custom_middleware.html
│ │ ├── async_middleware.html
│ │ ├── async_middleware_error_handler.html
│ │ ├── attaching_function_token
│ │ │ ├── async_attaching_function_token.html
│ │ │ ├── attaching_function_token.html
│ │ │ └── index.html
│ │ ├── authorization
│ │ │ ├── async_authorization.html
│ │ │ ├── async_internals.html
│ │ │ ├── async_multi_teams_authorization.html
│ │ │ ├── async_single_team_authorization.html
│ │ │ ├── authorization.html
│ │ │ ├── index.html
│ │ │ ├── internals.html
│ │ │ ├── multi_teams_authorization.html
│ │ │ └── single_team_authorization.html
│ │ ├── custom_middleware.html
│ │ ├── ignoring_self_events
│ │ │ ├── async_ignoring_self_events.html
│ │ │ ├── ignoring_self_events.html
│ │ │ └── index.html
│ │ ├── index.html
│ │ ├── message_listener_matches
│ │ │ ├── async_message_listener_matches.html
│ │ │ ├── index.html
│ │ │ └── message_listener_matches.html
│ │ ├── middleware.html
│ │ ├── middleware_error_handler.html
│ │ ├── request_verification
│ │ │ ├── async_request_verification.html
│ │ │ ├── index.html
│ │ │ └── request_verification.html
│ │ ├── ssl_check
│ │ │ ├── async_ssl_check.html
│ │ │ ├── index.html
│ │ │ └── ssl_check.html
│ │ └── url_verification
│ │ │ ├── async_url_verification.html
│ │ │ ├── index.html
│ │ │ └── url_verification.html
│ │ ├── oauth
│ │ ├── async_callback_options.html
│ │ ├── async_internals.html
│ │ ├── async_oauth_flow.html
│ │ ├── async_oauth_settings.html
│ │ ├── callback_options.html
│ │ ├── index.html
│ │ ├── internals.html
│ │ ├── oauth_flow.html
│ │ └── oauth_settings.html
│ │ ├── request
│ │ ├── async_internals.html
│ │ ├── async_request.html
│ │ ├── index.html
│ │ ├── internals.html
│ │ ├── payload_utils.html
│ │ └── request.html
│ │ ├── response
│ │ ├── index.html
│ │ └── response.html
│ │ ├── util
│ │ ├── async_utils.html
│ │ ├── index.html
│ │ └── utils.html
│ │ ├── version.html
│ │ └── workflows
│ │ ├── index.html
│ │ └── step
│ │ ├── async_step.html
│ │ ├── async_step_middleware.html
│ │ ├── index.html
│ │ ├── internals.html
│ │ ├── step.html
│ │ ├── step_middleware.html
│ │ └── utilities
│ │ ├── async_complete.html
│ │ ├── async_configure.html
│ │ ├── async_fail.html
│ │ ├── async_update.html
│ │ ├── complete.html
│ │ ├── configure.html
│ │ ├── fail.html
│ │ ├── index.html
│ │ └── update.html
│ └── img
│ ├── bolt-logo.svg
│ ├── bolt-py-logo.svg
│ ├── boltpy
│ ├── basic-information-page.png
│ ├── bolt-favicon.png
│ ├── bot-token.png
│ ├── ngrok.gif
│ ├── request-url-config.png
│ └── signing-secret.png
│ ├── favicon.ico
│ ├── slack-logo-on-white.png
│ ├── slack-logo.svg
│ └── tutorials
│ ├── ai-chatbot
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ ├── 7.png
│ └── 8.png
│ ├── custom-steps-jira
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 6.png
│ └── 7.png
│ ├── custom-steps-wfb-existing
│ ├── add-step.png
│ ├── app-message.png
│ ├── define-step.png
│ ├── find-step.png
│ ├── inputs.png
│ ├── org-ready.png
│ ├── outputs.png
│ └── step-inputs.png
│ ├── custom-steps-wfb-new
│ ├── app-token.png
│ ├── bot-token.png
│ ├── install.png
│ ├── manifest.png
│ ├── wfb-1.png
│ ├── wfb-10.png
│ ├── wfb-11.png
│ ├── wfb-12.png
│ ├── wfb-2.png
│ ├── wfb-3.png
│ ├── wfb-4.png
│ ├── wfb-5.png
│ ├── wfb-6.png
│ ├── wfb-7.png
│ ├── wfb-8.png
│ ├── wfb-9.png
│ └── workflow-step.png
│ └── modals
│ ├── base_link.gif
│ ├── final_product.gif
│ ├── heart_icon.gif
│ ├── interactivity_url.png
│ └── slash_command.png
├── examples
├── aiohttp_devtools
│ ├── async_app.py
│ └── requirements.txt
├── app.py
├── app_authorize.py
├── asgi
│ ├── app.py
│ ├── async_app.py
│ ├── async_oauth_app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── assistants
│ ├── app.py
│ ├── async_app.py
│ ├── async_interaction_app.py
│ └── interaction_app.py
├── async_app.py
├── async_app_authorize.py
├── aws_chalice
│ ├── .chalice
│ │ ├── config.json.oauth
│ │ └── config.json.simple
│ ├── .gitignore
│ ├── app.py
│ ├── deploy.sh
│ ├── oauth_app.py
│ ├── requirements.txt
│ └── simple_app.py
├── aws_lambda
│ ├── .env.oauth_sample
│ ├── .env.sample
│ ├── .gitignore
│ ├── README.md
│ ├── aws_lambda.py
│ ├── aws_lambda_config.yaml
│ ├── aws_lambda_oauth.py
│ ├── aws_lambda_oauth_config.yaml
│ ├── deploy.sh
│ ├── deploy_lazy.sh
│ ├── deploy_oauth.sh
│ ├── lazy_aws_lambda.py
│ ├── lazy_aws_lambda_config.yaml
│ ├── requirements.txt
│ └── requirements_oauth.txt
├── bottle
│ ├── app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── cherrypy
│ ├── app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── dialogs_app.py
├── django
│ ├── .gitignore
│ ├── README.md
│ ├── manage.py
│ ├── myslackapp
│ │ ├── __init__.py
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ ├── mysql-docker-compose.yml
│ ├── oauth_app
│ │ ├── __init__.py
│ │ ├── apps.py
│ │ ├── migrations
│ │ │ ├── 0001_initial.py
│ │ │ ├── 0002_token_rotation.py
│ │ │ └── __init__.py
│ │ ├── models.py
│ │ ├── slack_datastores.py
│ │ ├── slack_listeners.py
│ │ └── urls.py
│ ├── requirements.txt
│ └── simple_app
│ │ ├── __init__.py
│ │ ├── apps.py
│ │ ├── migrations
│ │ └── __init__.py
│ │ ├── models.py
│ │ ├── slack_listeners.py
│ │ └── urls.py
├── docker
│ ├── aiohttp
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── asgi
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── fastapi-gunicorn
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── flask-gunicorn
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── flask-uwsgi
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ ├── requirements.txt
│ │ └── uwsgi.ini
│ └── sanic
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
├── events_app.py
├── falcon
│ ├── app.py
│ ├── async_app.py
│ ├── async_oauth_app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── fastapi
│ ├── app.py
│ ├── async_app.py
│ ├── async_app_custom_props.py
│ ├── async_oauth_app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── flask
│ ├── app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── getting_started
│ ├── .gitignore
│ ├── README.md
│ ├── app.py
│ └── requirements.txt
├── google_app_engine
│ └── flask
│ │ ├── .gcloudignore
│ │ ├── .gitignore
│ │ ├── app.yaml
│ │ ├── env_variables.yaml.sample
│ │ ├── main.py
│ │ └── requirements.txt
├── google_cloud_functions
│ ├── .env.yaml.oauth-sample
│ ├── .env.yaml.sample
│ ├── .gcloudignore
│ ├── .gitignore
│ ├── datastore.py
│ ├── oauth_main.py
│ ├── requirements.txt
│ └── simple_main.py
├── google_cloud_run
│ ├── aiohttp
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
│ ├── flask-gunicorn
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
│ └── sanic
│ │ ├── Dockerfile
│ │ ├── main.py
│ │ └── requirements.txt
├── heroku
│ ├── Procfile
│ ├── main.py
│ └── requirements.txt
├── lazy_async_modals_app.py
├── lazy_modals_app.py
├── message_events.py
├── modals_app.py
├── modals_app_typed.py
├── oauth_app.py
├── oauth_app_settings.py
├── oauth_sqlite3_app.py
├── oauth_sqlite3_app_bot_only.py
├── oauth_sqlite3_app_org_level.py
├── pyramid
│ ├── app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── readme_app.py
├── readme_async_app.py
├── sanic
│ ├── async_app.py
│ ├── async_oauth_app.py
│ └── requirements.txt
├── socket_mode.py
├── socket_mode_async.py
├── socket_mode_async_healthcheck.py
├── socket_mode_healthcheck.py
├── socket_mode_oauth.py
├── socket_mode_proxy.py
├── sqlalchemy
│ ├── async_oauth_app.py
│ ├── oauth_app.py
│ ├── requirements.txt
│ └── requirements_async.txt
├── starlette
│ ├── app.py
│ ├── async_app.py
│ ├── async_oauth_app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── tornado
│ ├── app.py
│ ├── async_app.py
│ ├── async_oauth_app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── workflow_steps
│ ├── async_steps_from_apps.py
│ ├── async_steps_from_apps_decorator.py
│ ├── async_steps_from_apps_primitive.py
│ ├── steps_from_apps.py
│ ├── steps_from_apps_decorator.py
│ └── steps_from_apps_primitive.py
└── wsgi
│ ├── app.py
│ ├── oauth_app.py
│ └── requirements.txt
├── logs
└── .gitkeep
├── pyproject.toml
├── requirements.txt
├── requirements
├── adapter.txt
├── adapter_testing.txt
├── async.txt
├── testing.txt
├── testing_without_asyncio.txt
└── tools.txt
├── scripts
├── build_pypi_package.sh
├── deploy_to_pypi_org.sh
├── deploy_to_test_pypi_org.sh
├── generate_api_docs.sh
├── install_all_and_run_tests.sh
├── run_flake8.sh
├── run_mypy.sh
├── run_tests.sh
└── uninstall_all.sh
├── setup.cfg
├── slack_bolt
├── __init__.py
├── adapter
│ ├── __init__.py
│ ├── aiohttp
│ │ └── __init__.py
│ ├── asgi
│ │ ├── __init__.py
│ │ ├── aiohttp
│ │ │ └── __init__.py
│ │ ├── async_handler.py
│ │ ├── base_handler.py
│ │ ├── builtin
│ │ │ └── __init__.py
│ │ ├── http_request.py
│ │ ├── http_response.py
│ │ └── utils.py
│ ├── aws_lambda
│ │ ├── __init__.py
│ │ ├── chalice_handler.py
│ │ ├── chalice_lazy_listener_runner.py
│ │ ├── handler.py
│ │ ├── internals.py
│ │ ├── lambda_s3_oauth_flow.py
│ │ ├── lazy_listener_runner.py
│ │ └── local_lambda_client.py
│ ├── bottle
│ │ ├── __init__.py
│ │ └── handler.py
│ ├── cherrypy
│ │ ├── __init__.py
│ │ └── handler.py
│ ├── django
│ │ ├── __init__.py
│ │ └── handler.py
│ ├── falcon
│ │ ├── __init__.py
│ │ ├── async_resource.py
│ │ └── resource.py
│ ├── fastapi
│ │ ├── __init__.py
│ │ └── async_handler.py
│ ├── flask
│ │ ├── __init__.py
│ │ └── handler.py
│ ├── google_cloud_functions
│ │ ├── __init__.py
│ │ └── handler.py
│ ├── pyramid
│ │ ├── __init__.py
│ │ └── handler.py
│ ├── sanic
│ │ ├── __init__.py
│ │ └── async_handler.py
│ ├── socket_mode
│ │ ├── __init__.py
│ │ ├── aiohttp
│ │ │ └── __init__.py
│ │ ├── async_base_handler.py
│ │ ├── async_handler.py
│ │ ├── async_internals.py
│ │ ├── base_handler.py
│ │ ├── builtin
│ │ │ └── __init__.py
│ │ ├── internals.py
│ │ ├── websocket_client
│ │ │ └── __init__.py
│ │ └── websockets
│ │ │ └── __init__.py
│ ├── starlette
│ │ ├── __init__.py
│ │ ├── async_handler.py
│ │ └── handler.py
│ ├── tornado
│ │ ├── __init__.py
│ │ ├── async_handler.py
│ │ └── handler.py
│ └── wsgi
│ │ ├── __init__.py
│ │ ├── handler.py
│ │ ├── http_request.py
│ │ ├── http_response.py
│ │ └── internals.py
├── app
│ ├── __init__.py
│ ├── app.py
│ ├── async_app.py
│ └── async_server.py
├── async_app.py
├── authorization
│ ├── __init__.py
│ ├── async_authorize.py
│ ├── async_authorize_args.py
│ ├── authorize.py
│ ├── authorize_args.py
│ └── authorize_result.py
├── context
│ ├── __init__.py
│ ├── ack
│ │ ├── __init__.py
│ │ ├── ack.py
│ │ ├── async_ack.py
│ │ └── internals.py
│ ├── assistant
│ │ ├── __init__.py
│ │ ├── assistant_utilities.py
│ │ ├── async_assistant_utilities.py
│ │ ├── internals.py
│ │ ├── thread_context
│ │ │ └── __init__.py
│ │ └── thread_context_store
│ │ │ ├── __init__.py
│ │ │ ├── async_store.py
│ │ │ ├── default_async_store.py
│ │ │ ├── default_store.py
│ │ │ ├── file
│ │ │ └── __init__.py
│ │ │ └── store.py
│ ├── async_context.py
│ ├── base_context.py
│ ├── complete
│ │ ├── __init__.py
│ │ ├── async_complete.py
│ │ └── complete.py
│ ├── context.py
│ ├── fail
│ │ ├── __init__.py
│ │ ├── async_fail.py
│ │ └── fail.py
│ ├── get_thread_context
│ │ ├── __init__.py
│ │ ├── async_get_thread_context.py
│ │ └── get_thread_context.py
│ ├── respond
│ │ ├── __init__.py
│ │ ├── async_respond.py
│ │ ├── internals.py
│ │ └── respond.py
│ ├── save_thread_context
│ │ ├── __init__.py
│ │ ├── async_save_thread_context.py
│ │ └── save_thread_context.py
│ ├── say
│ │ ├── __init__.py
│ │ ├── async_say.py
│ │ ├── internals.py
│ │ └── say.py
│ ├── set_status
│ │ ├── __init__.py
│ │ ├── async_set_status.py
│ │ └── set_status.py
│ ├── set_suggested_prompts
│ │ ├── __init__.py
│ │ ├── async_set_suggested_prompts.py
│ │ └── set_suggested_prompts.py
│ └── set_title
│ │ ├── __init__.py
│ │ ├── async_set_title.py
│ │ └── set_title.py
├── error
│ └── __init__.py
├── kwargs_injection
│ ├── __init__.py
│ ├── args.py
│ ├── async_args.py
│ ├── async_utils.py
│ └── utils.py
├── lazy_listener
│ ├── __init__.py
│ ├── async_internals.py
│ ├── async_runner.py
│ ├── asyncio_runner.py
│ ├── internals.py
│ ├── runner.py
│ └── thread_runner.py
├── listener
│ ├── __init__.py
│ ├── async_builtins.py
│ ├── async_listener.py
│ ├── async_listener_completion_handler.py
│ ├── async_listener_error_handler.py
│ ├── async_listener_start_handler.py
│ ├── asyncio_runner.py
│ ├── builtins.py
│ ├── custom_listener.py
│ ├── listener.py
│ ├── listener_completion_handler.py
│ ├── listener_error_handler.py
│ ├── listener_start_handler.py
│ └── thread_runner.py
├── listener_matcher
│ ├── __init__.py
│ ├── async_builtins.py
│ ├── async_listener_matcher.py
│ ├── builtins.py
│ ├── custom_listener_matcher.py
│ └── listener_matcher.py
├── logger
│ ├── __init__.py
│ └── messages.py
├── middleware
│ ├── __init__.py
│ ├── assistant
│ │ ├── __init__.py
│ │ ├── assistant.py
│ │ └── async_assistant.py
│ ├── async_builtins.py
│ ├── async_custom_middleware.py
│ ├── async_middleware.py
│ ├── async_middleware_error_handler.py
│ ├── attaching_function_token
│ │ ├── __init__.py
│ │ ├── async_attaching_function_token.py
│ │ └── attaching_function_token.py
│ ├── authorization
│ │ ├── __init__.py
│ │ ├── async_authorization.py
│ │ ├── async_internals.py
│ │ ├── async_multi_teams_authorization.py
│ │ ├── async_single_team_authorization.py
│ │ ├── authorization.py
│ │ ├── internals.py
│ │ ├── multi_teams_authorization.py
│ │ └── single_team_authorization.py
│ ├── custom_middleware.py
│ ├── ignoring_self_events
│ │ ├── __init__.py
│ │ ├── async_ignoring_self_events.py
│ │ └── ignoring_self_events.py
│ ├── message_listener_matches
│ │ ├── __init__.py
│ │ ├── async_message_listener_matches.py
│ │ └── message_listener_matches.py
│ ├── middleware.py
│ ├── middleware_error_handler.py
│ ├── request_verification
│ │ ├── __init__.py
│ │ ├── async_request_verification.py
│ │ └── request_verification.py
│ ├── ssl_check
│ │ ├── __init__.py
│ │ ├── async_ssl_check.py
│ │ └── ssl_check.py
│ └── url_verification
│ │ ├── __init__.py
│ │ ├── async_url_verification.py
│ │ └── url_verification.py
├── oauth
│ ├── __init__.py
│ ├── async_callback_options.py
│ ├── async_internals.py
│ ├── async_oauth_flow.py
│ ├── async_oauth_settings.py
│ ├── callback_options.py
│ ├── internals.py
│ ├── oauth_flow.py
│ └── oauth_settings.py
├── py.typed
├── request
│ ├── __init__.py
│ ├── async_internals.py
│ ├── async_request.py
│ ├── internals.py
│ ├── payload_utils.py
│ └── request.py
├── response
│ ├── __init__.py
│ └── response.py
├── util
│ ├── __init__.py
│ ├── async_utils.py
│ └── utils.py
├── version.py
└── workflows
│ ├── __init__.py
│ └── step
│ ├── __init__.py
│ ├── async_step.py
│ ├── async_step_middleware.py
│ ├── internals.py
│ ├── step.py
│ ├── step_middleware.py
│ └── utilities
│ ├── __init__.py
│ ├── async_complete.py
│ ├── async_configure.py
│ ├── async_fail.py
│ ├── async_update.py
│ ├── complete.py
│ ├── configure.py
│ ├── fail.py
│ └── update.py
└── tests
├── __init__.py
├── adapter_tests
├── __init__.py
├── asgi
│ ├── __init__.py
│ ├── test_asgi_http.py
│ └── test_asgi_lifespan.py
├── aws
│ ├── __init__.py
│ ├── test_aws_chalice.py
│ ├── test_aws_lambda.py
│ └── test_lambda_s3_oauth_flow.py
├── bottle
│ ├── __init__.py
│ ├── test_bottle.py
│ └── test_bottle_oauth.py
├── cherrypy
│ ├── __init__.py
│ ├── test_cherrypy.py
│ └── test_cherrypy_oauth.py
├── django
│ ├── __init__.py
│ ├── test_django.py
│ └── test_django_settings.py
├── falcon
│ ├── __init__.py
│ └── test_falcon.py
├── flask
│ ├── __init__.py
│ └── test_flask.py
├── google_cloud_functions
│ ├── __init__.py
│ └── test_google_cloud_functions.py
├── pyramid
│ ├── __init__.py
│ └── test_pyramid.py
├── socket_mode
│ ├── __init__.py
│ ├── mock_socket_mode_server.py
│ ├── mock_web_api_server.py
│ ├── test_interactions_builtin.py
│ ├── test_interactions_websocket_client.py
│ └── test_lazy_listeners.py
├── starlette
│ ├── __init__.py
│ ├── test_fastapi.py
│ └── test_starlette.py
├── tornado
│ ├── __init__.py
│ ├── test_tornado.py
│ └── test_tornado_oauth.py
└── wsgi
│ ├── __init__.py
│ └── test_wsgi_http.py
├── adapter_tests_async
├── __init__.py
├── socket_mode
│ ├── __init__.py
│ ├── test_async_aiohttp.py
│ ├── test_async_lazy_listeners.py
│ └── test_async_websockets.py
├── test_async_asgi.py
├── test_async_falcon.py
├── test_async_fastapi.py
├── test_async_sanic.py
├── test_async_starlette.py
├── test_tornado.py
└── test_tornado_oauth.py
├── mock_asgi_server.py
├── mock_web_api_server
├── __init__.py
├── mock_handler.py
├── mock_server_thread.py
└── received_requests.py
├── mock_wsgi_server.py
├── scenario_tests
├── __init__.py
├── test_app.py
├── test_app_actor_user_token.py
├── test_app_bot_only.py
├── test_app_custom_authorize.py
├── test_app_decorators.py
├── test_app_installation_store.py
├── test_app_using_methods_in_class.py
├── test_attachment_actions.py
├── test_authorize.py
├── test_block_actions.py
├── test_block_actions_respond.py
├── test_block_suggestion.py
├── test_dialogs.py
├── test_error_handler.py
├── test_events.py
├── test_events_assistant.py
├── test_events_ignore_self.py
├── test_events_org_apps.py
├── test_events_request_verification.py
├── test_events_shared_channels.py
├── test_events_socket_mode.py
├── test_events_token_revocations.py
├── test_events_url_verification.py
├── test_function.py
├── test_installation_store_authorize.py
├── test_lazy.py
├── test_listener_middleware.py
├── test_message.py
├── test_message_bot.py
├── test_message_changed.py
├── test_message_deleted.py
├── test_message_file_share.py
├── test_message_thread_broadcast.py
├── test_middleware.py
├── test_shortcut.py
├── test_slash_command.py
├── test_ssl_check.py
├── test_view_closed.py
├── test_view_submission.py
├── test_web_client_customization.py
├── test_workflow_steps.py
├── test_workflow_steps_decorator_simple.py
└── test_workflow_steps_decorator_with_args.py
├── scenario_tests_async
├── __init__.py
├── test_app.py
├── test_app_actor_user_token.py
├── test_app_bot_only.py
├── test_app_custom_authorize.py
├── test_app_decorators.py
├── test_app_dispatch.py
├── test_app_installation_store.py
├── test_app_using_methods_in_class.py
├── test_attachment_actions.py
├── test_authorize.py
├── test_block_actions.py
├── test_block_actions_respond.py
├── test_block_suggestion.py
├── test_dialogs.py
├── test_error_handler.py
├── test_events.py
├── test_events_assistant.py
├── test_events_ignore_self.py
├── test_events_org_apps.py
├── test_events_request_verification.py
├── test_events_shared_channels.py
├── test_events_socket_mode.py
├── test_events_token_revocations.py
├── test_events_url_verification.py
├── test_function.py
├── test_installation_store_authorize.py
├── test_lazy.py
├── test_listener_middleware.py
├── test_message.py
├── test_message_bot.py
├── test_message_changed.py
├── test_message_deleted.py
├── test_message_file_share.py
├── test_message_thread_broadcast.py
├── test_middleware.py
├── test_shortcut.py
├── test_slash_command.py
├── test_ssl_check.py
├── test_view_closed.py
├── test_view_submission.py
├── test_web_client_customization.py
├── test_workflow_steps.py
├── test_workflow_steps_decorator_simple.py
└── test_workflow_steps_decorator_with_args.py
├── slack_bolt
├── __init__.py
├── app
│ ├── __init__.py
│ └── test_dev_server.py
├── authorization
│ ├── __init__.py
│ └── test_authorize.py
├── context
│ ├── __init__.py
│ ├── test_ack.py
│ ├── test_complete.py
│ ├── test_fail.py
│ ├── test_respond.py
│ ├── test_respond_internals.py
│ └── test_say.py
├── error
│ ├── __init__.py
│ └── test_errors.py
├── kwargs_injection
│ ├── __init__.py
│ └── test_args.py
├── listener
│ ├── __init__.py
│ ├── test_listener_completion_handler.py
│ └── test_listener_start_handler.py
├── listener_matcher
│ ├── __init__.py
│ ├── test_builtins.py
│ └── test_custom_listener_matcher.py
├── logger
│ ├── __init__.py
│ └── test_unmatched_suggestions.py
├── middleware
│ ├── __init__.py
│ ├── authorization
│ │ ├── __init__.py
│ │ └── test_single_team_authorization.py
│ └── request_verification
│ │ ├── __init__.py
│ │ └── test_request_verification.py
├── oauth
│ ├── __init__.py
│ ├── test_internals.py
│ ├── test_oauth_flow.py
│ └── test_oauth_flow_sqlite3.py
├── request
│ ├── __init__.py
│ ├── test_internals.py
│ └── test_request.py
├── util
│ ├── __init__.py
│ └── test_util.py
└── workflows
│ ├── __init__.py
│ └── step
│ ├── __init__.py
│ └── test_step.py
├── slack_bolt_async
├── __init__.py
├── app
│ ├── __init__.py
│ └── test_server.py
├── authorization
│ ├── __init__.py
│ └── test_async_authorize.py
├── context
│ ├── __init__.py
│ ├── test_async_ack.py
│ ├── test_async_complete.py
│ ├── test_async_fail.py
│ ├── test_async_respond.py
│ └── test_async_say.py
├── kwargs_injection
│ ├── __init__.py
│ └── test_async_args.py
├── listener
│ ├── __init__.py
│ ├── test_async_listener_completion_handler.py
│ └── test_async_listener_start_handler.py
├── listener_matcher
│ ├── __init__.py
│ └── test_async_custom_listener_matcher.py
├── logger
│ ├── __init__.py
│ └── test_unmatched_suggestions.py
├── middleware
│ ├── __init__.py
│ ├── authorization
│ │ ├── __init__.py
│ │ └── test_single_team_authorization.py
│ └── request_verification
│ │ ├── __init__.py
│ │ └── test_request_verification.py
├── oauth
│ ├── __init__.py
│ ├── test_async_oauth_flow.py
│ └── test_async_oauth_flow_sqlite3.py
├── request
│ ├── __init__.py
│ └── test_async_request.py
└── workflows
│ ├── __init__.py
│ └── step
│ ├── __init__.py
│ └── test_async_step.py
└── utils.py
/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | max-line-length = 125
3 | ignore = F841,F821,W503,E402
4 |
--------------------------------------------------------------------------------
/.git-blame-ignore-revs:
--------------------------------------------------------------------------------
1 | # change black settings
2 | 0e4cd56b69e8f83166cd262f762802b7f18c3d21
3 |
--------------------------------------------------------------------------------
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Introduction
4 |
5 | Diversity and inclusion make our community strong. We encourage participation from the most varied and diverse backgrounds possible and want to be very clear about where we stand.
6 |
7 | Our goal is to maintain a safe, helpful and friendly community for everyone, regardless of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other defining characteristic.
8 |
9 | This code and related procedures also apply to unacceptable behavior occurring outside the scope of community activities, in all community venues (online and in-person) as well as in all one-on-one communications, and anywhere such behavior has the potential to adversely affect the safety and well-being of community members.
10 |
11 | For more information on our code of conduct, please visit [https://slackhq.github.io/code-of-conduct](https://slackhq.github.io/code-of-conduct)
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/02_enhancement.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Enhancement / Feature Request
3 | about: Submit an enhancement/feature request
4 | title: (Set a clear title describing your idea)
5 | labels: 'untriaged'
6 | assignees: ''
7 | ---
8 |
9 | (Describe your issue and goal here)
10 |
11 | ### Category (place an `x` in each of the `[ ]`)
12 |
13 | * [ ] **slack_bolt.App** and/or its core components
14 | * [ ] **slack_bolt.async_app.AsyncApp** and/or its core components
15 | * [ ] Adapters in **slack_bolt.adapter**
16 | * [ ] Others
17 |
18 | ## Requirements
19 |
20 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/03_document.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: SDK Document
3 | about: Submit an issue on documents
4 | title: (Set a clear title describing your idea)
5 | labels: 'untriaged'
6 | assignees: ''
7 | ---
8 |
9 | (Describe your issue and goal here)
10 |
11 | ### The page URLs
12 |
13 | * https://slack.dev/bolt-python/
14 |
15 | ## Requirements
16 |
17 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Slack Platform Customer Support
4 | url: https://my.slack.com/help/requests/new
5 | about: |
6 | This issue tracker is a place to track bugs, feature requests, and questions on this SDK side.
7 |
8 | If you have a general question on how to use the Slack platform, please get in touch with our customer support agents first via either /feedback in your Slack workspace or the help page link here.
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Please see the documentation for all configuration options:
2 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3 |
4 | version: 2
5 | updates:
6 | - package-ecosystem: "pip"
7 | directory: "/"
8 | schedule:
9 | interval: "monthly"
10 | open-pull-requests-limit: 5
11 | - package-ecosystem: "github-actions"
12 | directory: "/"
13 | schedule:
14 | interval: "monthly"
15 | - package-ecosystem: "npm"
16 | directory: "/docs"
17 | schedule:
18 | interval: "monthly"
19 | groups:
20 | docusaurus:
21 | patterns:
22 | - "@docusaurus/*"
23 | react:
24 | patterns:
25 | - "react"
26 | - "react-dom"
27 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | (Describe your issue and goal here)
2 |
3 | ### Reproducible in:
4 |
5 | ```bash
6 | pip freeze | grep slack
7 | python --version
8 | sw_vers && uname -v # or `ver`
9 | ```
10 |
11 | #### The `slack_bolt` version
12 |
13 | (Paste the output of `pip freeze | grep slack`)
14 |
15 | #### Python runtime version
16 |
17 | (Paste the output of `python --version`)
18 |
19 | #### OS info
20 |
21 | (Paste the output of `sw_vers && uname -v` on macOS/Linux or `ver` on Windows OS)
22 |
23 | #### Steps to reproduce:
24 |
25 | (Share the commands to run, source code, and project settings (e.g., setup.py))
26 |
27 | 1.
28 | 2.
29 | 3.
30 |
31 | ### Expected result:
32 |
33 | (Tell what you expected to happen)
34 |
35 | ### Actual result:
36 |
37 | (Tell what actually happened with logs, screenshots)
38 |
39 | ## Requirements
40 |
41 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
42 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Summary
2 |
3 |
4 |
5 | ### Testing
6 |
7 |
8 |
9 | ### Category
10 |
11 | * [ ] `slack_bolt.App` and/or its core components
12 | * [ ] `slack_bolt.async_app.AsyncApp` and/or its core components
13 | * [ ] Adapters in `slack_bolt.adapter`
14 | * [ ] Document pages under `/docs`
15 | * [ ] Others
16 |
17 | ## Requirements
18 |
19 | Please read the [Contributing guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.
20 |
21 | * [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackapi/bolt-python/blob/main/.github/contributing.md) and have done my best effort to follow them.
22 | * [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
23 | * [ ] I've run `./scripts/install_all_and_run_tests.sh` after making the changes.
24 |
--------------------------------------------------------------------------------
/.github/workflows/flake8.yml:
--------------------------------------------------------------------------------
1 | name: Run flake8 validation
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | timeout-minutes: 20
13 | strategy:
14 | matrix:
15 | python-version: ["3.13"]
16 | permissions:
17 | contents: read
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | with:
21 | persist-credentials: false
22 | - name: Set up Python ${{ matrix.python-version }}
23 | uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
24 | with:
25 | python-version: ${{ matrix.python-version }}
26 | - name: Run flake8 verification
27 | run: |
28 | ./scripts/run_flake8.sh
29 |
--------------------------------------------------------------------------------
/.github/workflows/mypy.yml:
--------------------------------------------------------------------------------
1 | name: Run mypy validation
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | timeout-minutes: 20
13 | strategy:
14 | matrix:
15 | python-version: ["3.13"]
16 | permissions:
17 | contents: read
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | with:
21 | persist-credentials: false
22 | - name: Set up Python ${{ matrix.python-version }}
23 | uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
24 | with:
25 | python-version: ${{ matrix.python-version }}
26 | - name: Run mypy verification
27 | run: |
28 | ./scripts/run_mypy.sh
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # general things to ignore
2 | build/
3 | dist/
4 | docs/_sources/
5 | docs/.doctrees
6 | .eggs/
7 | *.egg-info/
8 | *.egg
9 | *.py[cod]
10 | __pycache__/
11 | *.so
12 | *~
13 |
14 | # virtualenv
15 | env*/
16 | venv/
17 | .venv*
18 | .env/
19 |
20 | # codecov / coverage
21 | .coverage
22 | cov_*
23 | coverage.xml
24 | reports/
25 |
26 | # due to using tox and pytest
27 | .tox
28 | .cache
29 | .pytest_cache/
30 | .python-version
31 | pip
32 | .mypy_cache/
33 | .ruby-version
34 |
35 | # JetBrains PyCharm settings
36 | .idea/
37 |
38 | tmp.txt
39 | .DS_Store
40 | logs/
41 |
42 | .env*
43 | *.db
44 |
45 | .pytype/
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020- Slack Technologies, LLC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include README.md LICENSE slack_bolt/py.typed
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | coverage:
2 | status:
3 | project:
4 | default:
5 | threshold: 2.0%
6 | patch:
7 | default:
8 | target: 50%
9 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .docusaurus
3 | build
--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 |
--------------------------------------------------------------------------------
/docs/content/concepts/commands.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Listening & responding to commands
3 | lang: en
4 | slug: /concepts/commands
5 | ---
6 |
7 | Your app can use the `command()` method to listen to incoming slash command requests. The method requires a `command_name` of type `str`.
8 |
9 | Commands must be acknowledged with `ack()` to inform Slack your app has received the request.
10 |
11 | There are two ways to respond to slash commands. The first way is to use `say()`, which accepts a string or JSON payload. The second is `respond()` which is a utility for the `response_url`. These are explained in more depth in the [responding to actions](/concepts/actions) section.
12 |
13 | When setting up commands within your app configuration, you'll append `/slack/events` to your request URL.
14 |
15 | Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments.
16 | ```python
17 | # The echo command simply echoes on command
18 | @app.command("/echo")
19 | def repeat_text(ack, respond, command):
20 | # Acknowledge command request
21 | ack()
22 | respond(f"{command['text']}")
23 | ```
24 |
--------------------------------------------------------------------------------
/docs/content/concepts/errors.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Handling errors
3 | lang: en
4 | slug: /concepts/errors
5 | ---
6 |
7 | If an error occurs in a listener, you can handle it directly using a try/except block. Errors associated with your app will be of type `BoltError`. Errors associated with calling Slack APIs will be of type `SlackApiError`.
8 |
9 | By default, the global error handler will log all non-handled exceptions to the console. To handle global errors yourself, you can attach a global error handler to your app using the `app.error(fn)` function.
10 |
11 | ```python
12 | @app.error
13 | def custom_error_handler(error, body, logger):
14 | logger.exception(f"Error: {error}")
15 | logger.info(f"Request body: {body}")
16 | ```
--------------------------------------------------------------------------------
/docs/content/concepts/logging.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Logging
3 | lang: en
4 | slug: /concepts/logging
5 | ---
6 |
7 | By default, Bolt will log information from your app to the output destination. After you've imported the `logging` module, you can customize the root log level by passing the `level` parameter to `basicConfig()`. The available log levels in order of least to most severe are `debug`, `info`, `warning`, `error`, and `critical`.
8 |
9 | Outside of a global context, you can also log a single message corresponding to a specific level. Because Bolt uses Python’s [standard logging module](https://docs.python.org/3/library/logging.html), you can use any its features.
10 |
11 | ```python
12 | import logging
13 |
14 | # logger in a global context
15 | # requires importing logging
16 | logging.basicConfig(level=logging.DEBUG)
17 |
18 | @app.event("app_mention")
19 | def handle_mention(body, say, logger):
20 | user = body["event"]["user"]
21 | # single logger call
22 | # global logger is passed to listener
23 | logger.debug(body)
24 | say(f"{user} mentioned your app")
25 | ```
26 |
--------------------------------------------------------------------------------
/docs/content/concepts/token-rotation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Token rotation
3 | lang: en
4 | slug: /concepts/token-rotation
5 | ---
6 |
7 | Supported in Bolt for Python as of [v1.7.0](https://github.com/slackapi/bolt-python/releases/tag/v1.7.0), token rotation provides an extra layer of security for your access tokens and is defined by the [OAuth V2 RFC](https://datatracker.ietf.org/doc/html/rfc6749#section-10.4).
8 |
9 | Instead of an access token representing an existing installation of your Slack app indefinitely, with token rotation enabled, access tokens expire. A refresh token acts as a long-lived way to refresh your access tokens.
10 |
11 | Bolt for Python supports and will handle token rotation automatically so long as the [built-in OAuth](/concepts/authenticating-oauth) functionality is used.
12 |
13 | For more information about token rotation, please see the [documentation](https://docs.slack.dev/authentication/using-token-rotation).
--------------------------------------------------------------------------------
/docs/content/concepts/web-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Using the Web API
3 | lang: en
4 | slug: /concepts/web-api
5 | ---
6 |
7 | You can call [any Web API method](https://docs.slack.dev/reference/methods) using the [`WebClient`](https://tools.slack.dev/python-slack-sdk/web) provided to your Bolt app as either `app.client` or `client` in middleware/listener arguments (given that your app has the appropriate scopes). When you call one the client's methods, it returns a `SlackResponse` which contains the response from Slack.
8 |
9 | The token used to initialize Bolt can be found in the `context` object, which is required to call most Web API methods.
10 |
11 | :::info
12 |
13 | Refer to [the module document](https://tools.slack.dev/bolt-python/api-docs/slack_bolt/kwargs_injection/args.html) to learn the available listener arguments.
14 |
15 | :::
16 |
17 | ```python
18 | @app.message("wake me up")
19 | def say_hello(client, message):
20 | # Unix Epoch time for September 30, 2020 11:59:59 PM
21 | when_september_ends = 1601510399
22 | channel_id = message["channel"]
23 | client.chat_scheduleMessage(
24 | channel=channel_id,
25 | post_at=when_september_ends,
26 | text="Summer has come and passed"
27 | )
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/footerConfig.js:
--------------------------------------------------------------------------------
1 | const footer = {
2 | links: [
3 | {
4 | items: [
5 | {
6 | html: `
7 |
11 |
12 | ©2025 Slack Technologies, LLC, a Salesforce company. All rights reserved. Various trademarks held by their respective owners.
13 |
14 | `,
15 | },
16 | ],
17 | },
18 | ],
19 | };
20 |
21 | module.exports = footer;
22 |
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/commands.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: コマンドのリスニングと応答
3 | lang: ja-jp
4 | slug: /concepts/commands
5 | ---
6 |
7 | スラッシュコマンドが実行されたリクエストをリッスンするには、`command()` メソッドを使用します。このメソッドでは `str` 型の `command_name` の指定が必要です。
8 |
9 | コマンドリクエストをアプリが受信し確認したことを Slack に通知するため、`ack()` を呼び出す必要があります。
10 |
11 | スラッシュコマンドに応答する方法は 2 つあります。1 つ目は `say()` を使う方法で、文字列または JSON のペイロードを渡すことができます。2 つ目は `respond()` を使う方法です。これは `response_url` がある場合に活躍します。これらの方法は[アクションへの応答](/concepts/action-respond)セクションで詳しく説明しています。
12 |
13 | アプリの設定でコマンドを登録するときは、リクエスト URL の末尾に `/slack/events` をつけます。
14 |
15 | 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。
16 | ```python
17 | # echoコマンドは受け取ったコマンドをそのまま返す
18 | @app.command("/echo")
19 | def repeat_text(ack, respond, command):
20 | # command リクエストを確認
21 | ack()
22 | respond(f"{command['text']}")
23 | ```
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/errors.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: エラーの処理
3 | lang: ja-jp
4 | slug: /concepts/errors
5 | ---
6 |
7 | リスナー内でエラーが発生した場合に try/except ブロックを使用して直接エラーを処理することができます。アプリに関連するエラーは、`BoltError` 型です。Slack API の呼び出しに関連するエラーは、`SlackApiError` 型となります。
8 |
9 | デフォルトでは、すべての処理されなかった例外のログはグローバルのエラーハンドラーによってコンソールに出力されます。グローバルのエラーを開発者自身で処理するには、`app.error(fn)` 関数を使ってグローバルのエラーハンドラーをアプリに設定します。
10 |
11 | ```python
12 | @app.error
13 | def custom_error_handler(error, body, logger):
14 | logger.exception(f"Error: {error}")
15 | logger.info(f"Request body: {body}")
16 | ```
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/logging.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ロギング
3 | lang: ja-jp
4 | slug: /concepts/logging
5 | ---
6 |
7 | デフォルトでは、アプリからのログ情報は、既定の出力先に出力されます。`logging` モジュールをインポートすれば、`basicConfig()` の `level` パラメーターでrootのログレベルを変更することができます。指定できるログレベルは、重要度の低い方から `debug`、`info`、`warning`、`error`、および `critical` です。
8 |
9 | グローバルのコンテキストとは別に、指定のログレベルに応じて単一のメッセージをログ出力することもできます。Bolt では [Python 標準の logging モジュール](https://docs.python.org/3/library/logging.html)が使われているため、このモジュールが持つすべての機能を利用できます。
10 |
11 | ```python
12 | import logging
13 |
14 | # グローバルのコンテキストの logger です
15 | # logging をインポートする必要があります
16 | logging.basicConfig(level=logging.DEBUG)
17 |
18 | @app.event("app_mention")
19 | def handle_mention(body, say, logger):
20 | user = body["event"]["user"]
21 | # 単一の logger の呼び出しです
22 | # グローバルの logger がリスナーに渡されています
23 | logger.debug(body)
24 | say(f"{user} mentioned your app")
25 | ```
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/message-listening.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: メッセージのリスニング
3 | lang: ja-jp
4 | slug: /concepts/message-listening
5 | ---
6 |
7 | [あなたのアプリがアクセス権限を持つ](https://docs.slack.dev/messaging/retrieving-messages)メッセージの投稿イベントをリッスンするには `message()` メソッドを利用します。このメソッドは `type` が `message` ではないイベントを処理対象から除外します。
8 |
9 | `message()` の引数には `str` 型または `re.Pattern` オブジェクトを指定できます。この条件のパターンに一致しないメッセージは除外されます。
10 |
11 | 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。
12 | ```python
13 | # '👋' が含まれるすべてのメッセージに一致
14 | @app.message(":wave:")
15 | def say_hello(message, say):
16 | user = message['user']
17 | say(f"Hi there, <@{user}>!")
18 | ```
19 |
20 | ## 正規表現パターンの利用
21 |
22 | 文字列の代わりに `re.compile()` メソッドを使用すれば、より細やかな条件指定ができます。
23 |
24 | ```python
25 | import re
26 |
27 | @app.message(re.compile("(hi|hello|hey)"))
28 | def say_hello_regex(say, context):
29 | # 正規表現のマッチ結果は context.matches に設定される
30 | greeting = context['matches'][0]
31 | say(f"{greeting}, how are you?")
32 | ```
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/token-rotation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: トークンのローテーション
3 | lang: ja-jp
4 | slug: /concepts/token-rotation
5 | ---
6 |
7 | Bolt for Python [v1.7.0](https://github.com/slackapi/bolt-python/releases/tag/v1.7.0) から、アクセストークンのさらなるセキュリティ強化のレイヤーであるトークンローテーションの機能に対応しています。トークンローテーションは [OAuth V2 の RFC](https://datatracker.ietf.org/doc/html/rfc6749#section-10.4) で規定されているものです。
8 |
9 | 既存の Slack アプリではアクセストークンが無期限に存在し続けるのに対して、トークンローテーションを有効にしたアプリではアクセストークンが失効するようになります。リフレッシュトークンを利用して、アクセストークンを長期間にわたって更新し続けることができます。
10 |
11 | [Bolt for Python の組み込みの OAuth 機能](/concepts/authenticating-oauth) を使用していれば、Bolt for Python が自動的にトークンローテーションの処理をハンドリングします。
12 |
13 | トークンローテーションに関する詳細は [API ドキュメント](https://docs.slack.dev/authentication/using-token-rotation)を参照してください。
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-plugin-content-docs/current/concepts/web-api.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Web API の使い方
3 | lang: ja-jp
4 | slug: /concepts/web-api
5 | ---
6 |
7 | `app.client`、またはミドルウェア・リスナーの引数 `client` として Bolt アプリに提供されている [`WebClient`](https://tools.slack.dev/python-slack-sdk/basic_usage.html) は必要な権限を付与されており、これを利用することで[あらゆる Web API メソッド](https://docs.slack.dev/reference/methods)を呼び出すことができます。このクライアントのメソッドを呼び出すと `SlackResponse` という Slack からの応答情報を含むオブジェクトが返されます。
8 |
9 | Bolt の初期化に使用するトークンは `context` オブジェクトに設定されます。このトークンは、多くの Web API メソッドを呼び出す際に必要となります。
10 |
11 | 指定可能な引数の一覧はモジュールドキュメントを参考にしてください。
12 | ```python
13 | @app.message("wake me up")
14 | def say_hello(client, message):
15 | # 2020 年 9 月 30 日午後 11:59:59 を示す Unix エポック秒
16 | when_september_ends = 1601510399
17 | channel_id = message["channel"]
18 | client.chat_scheduleMessage(
19 | channel=channel_id,
20 | post_at=when_september_ends,
21 | text="Summer has come and passed"
22 | )
23 | ```
--------------------------------------------------------------------------------
/docs/i18n/ja-jp/docusaurus-theme-classic/footer.json:
--------------------------------------------------------------------------------
1 | {
2 | "copyright": {
3 | "message": " Made with ♡ by Slack and friends
",
4 | "description": "The footer copyright"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/docs/src/theme/NotFound/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {translate} from '@docusaurus/Translate';
3 | import {PageMetadata} from '@docusaurus/theme-common';
4 | import Layout from '@theme/Layout';
5 | import NotFoundContent from '@theme/NotFound/Content';
6 | export default function Index() {
7 | const title = translate({
8 | id: 'theme.NotFound.title',
9 | message: 'Page Not Found',
10 | });
11 | return (
12 | <>
13 |
14 |
15 |
16 |
17 | >
18 | );
19 | }
20 |
--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/.nojekyll
--------------------------------------------------------------------------------
/docs/static/img/bolt-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/static/img/bolt-py-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/static/img/boltpy/basic-information-page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/basic-information-page.png
--------------------------------------------------------------------------------
/docs/static/img/boltpy/bolt-favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/bolt-favicon.png
--------------------------------------------------------------------------------
/docs/static/img/boltpy/bot-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/bot-token.png
--------------------------------------------------------------------------------
/docs/static/img/boltpy/ngrok.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/ngrok.gif
--------------------------------------------------------------------------------
/docs/static/img/boltpy/request-url-config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/request-url-config.png
--------------------------------------------------------------------------------
/docs/static/img/boltpy/signing-secret.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/boltpy/signing-secret.png
--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/favicon.ico
--------------------------------------------------------------------------------
/docs/static/img/slack-logo-on-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/slack-logo-on-white.png
--------------------------------------------------------------------------------
/docs/static/img/slack-logo.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/1.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/2.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/3.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/4.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/5.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/6.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/7.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/ai-chatbot/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/ai-chatbot/8.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/1.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/2.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/3.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/4.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/5.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/6.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-jira/7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-jira/7.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/add-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/add-step.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/app-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/app-message.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/define-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/define-step.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/find-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/find-step.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/inputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/inputs.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/org-ready.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/org-ready.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/outputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/outputs.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-existing/step-inputs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-existing/step-inputs.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/app-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/app-token.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/bot-token.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/bot-token.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/install.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/install.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/manifest.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/manifest.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-1.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-10.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-11.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-12.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-2.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-3.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-4.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-5.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-6.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-7.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-8.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/wfb-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/wfb-9.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/custom-steps-wfb-new/workflow-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/custom-steps-wfb-new/workflow-step.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/modals/base_link.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/base_link.gif
--------------------------------------------------------------------------------
/docs/static/img/tutorials/modals/final_product.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/final_product.gif
--------------------------------------------------------------------------------
/docs/static/img/tutorials/modals/heart_icon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/heart_icon.gif
--------------------------------------------------------------------------------
/docs/static/img/tutorials/modals/interactivity_url.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/interactivity_url.png
--------------------------------------------------------------------------------
/docs/static/img/tutorials/modals/slash_command.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/docs/static/img/tutorials/modals/slash_command.png
--------------------------------------------------------------------------------
/examples/aiohttp_devtools/async_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt.async_app import AsyncApp
6 |
7 | app = AsyncApp()
8 |
9 |
10 | @app.event("app_mention")
11 | async def event_test(body, say, logger):
12 | logger.info(body)
13 | await say("What's up?")
14 |
15 |
16 | @app.command("/hello-bolt-python")
17 | # or app.command(re.compile(r"/hello-.+"))(test_command)
18 | async def command(ack, body):
19 | user_id = body["user_id"]
20 | await ack(f"Hi <@{user_id}>!")
21 |
22 |
23 | def app_factory():
24 | return app.web_app()
25 |
26 |
27 | # pip install -r requirements.txt
28 | # export SLACK_SIGNING_SECRET=***
29 | # export SLACK_BOT_TOKEN=xoxb-***
30 | # adev runserver --port 3000 --app-factory app_factory async_app.py
31 |
--------------------------------------------------------------------------------
/examples/aiohttp_devtools/requirements.txt:
--------------------------------------------------------------------------------
1 | aiohttp>=3,<4
2 | aiohttp-devtools>=0.13,<0.14
--------------------------------------------------------------------------------
/examples/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 |
7 | app = App()
8 |
9 |
10 | @app.middleware # or app.use(log_request)
11 | def log_request(logger, body, next):
12 | logger.debug(body)
13 | return next()
14 |
15 |
16 | @app.command("/hello-bolt-python")
17 | def hello_command(ack, body):
18 | user_id = body["user_id"]
19 | ack(f"Hi <@{user_id}>!")
20 |
21 |
22 | @app.event("app_mention")
23 | def event_test(body, say, logger):
24 | logger.info(body)
25 | say("What's up?")
26 |
27 |
28 | @app.error
29 | def global_error_handler(error, body, logger):
30 | logger.exception(error)
31 | logger.info(body)
32 |
33 |
34 | if __name__ == "__main__":
35 | app.start(3000)
36 |
37 | # pip install slack_bolt
38 | # export SLACK_SIGNING_SECRET=***
39 | # export SLACK_BOT_TOKEN=xoxb-***
40 | # python app.py
41 |
--------------------------------------------------------------------------------
/examples/app_authorize.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 | from slack_bolt import App
7 | from slack_bolt.authorization import AuthorizeResult
8 | from slack_sdk import WebClient
9 |
10 |
11 | def authorize(enterprise_id, team_id, user_id, client: WebClient, logger):
12 | logger.info(f"{enterprise_id},{team_id},{user_id}")
13 | # You can implement your own logic here
14 | token = os.environ["MY_TOKEN"]
15 | return AuthorizeResult.from_auth_test_response(
16 | auth_test_response=client.auth_test(token=token),
17 | bot_token=token,
18 | )
19 |
20 |
21 | app = App(signing_secret=os.environ["SLACK_SIGNING_SECRET"], authorize=authorize)
22 |
23 |
24 | @app.command("/hello-bolt-python")
25 | def hello_command(ack, body):
26 | user_id = body["user_id"]
27 | ack(f"Hi <@{user_id}>!")
28 |
29 |
30 | @app.event("app_mention")
31 | def event_test(body, say, logger):
32 | logger.info(body)
33 | say("What's up?")
34 |
35 |
36 | if __name__ == "__main__":
37 | app.start(3000)
38 |
39 | # pip install slack_bolt
40 | # export SLACK_SIGNING_SECRET=***
41 | # export MY_TOKEN=xoxb-***
42 | # python app_authorize.py
43 |
--------------------------------------------------------------------------------
/examples/asgi/app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.asgi import SlackRequestHandler
3 |
4 | app = App()
5 |
6 |
7 | @app.event("app_mention")
8 | def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | say("What's up?")
11 |
12 |
13 | api = SlackRequestHandler(app)
14 |
15 | # pip install -r requirements.txt
16 | # export SLACK_SIGNING_SECRET=***
17 | # export SLACK_BOT_TOKEN=xoxb-***
18 | # uvicorn app:api --reload --port 3000 --log-level debug
19 | # ngrok http 3000
20 |
--------------------------------------------------------------------------------
/examples/asgi/async_app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.async_app import AsyncApp
2 | from slack_bolt.adapter.asgi.async_handler import AsyncSlackRequestHandler
3 |
4 | app = AsyncApp()
5 |
6 |
7 | @app.event("app_mention")
8 | async def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | await say("What's up?")
11 |
12 |
13 | api = AsyncSlackRequestHandler(app)
14 |
15 | # pip install -r requirements.txt
16 | # export SLACK_SIGNING_SECRET=***
17 | # export SLACK_BOT_TOKEN=xoxb-***
18 | # uvicorn async_app:api --reload --port 3000 --log-level debug
19 | # ngrok http 3000
20 |
--------------------------------------------------------------------------------
/examples/asgi/async_oauth_app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.async_app import AsyncApp
2 | from slack_bolt.adapter.asgi.async_handler import AsyncSlackRequestHandler
3 |
4 | app = AsyncApp()
5 |
6 |
7 | @app.event("app_mention")
8 | async def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | await say("What's up?")
11 |
12 |
13 | api = AsyncSlackRequestHandler(app)
14 |
15 | # pip install -r requirements.txt
16 |
17 | # # -- OAuth flow -- #
18 | # export SLACK_SIGNING_SECRET=***
19 | # export SLACK_CLIENT_ID=111.111
20 | # export SLACK_CLIENT_SECRET=***
21 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
22 |
23 | # uvicorn async_oauth_app:api --reload --port 3000 --log-level debug
24 |
--------------------------------------------------------------------------------
/examples/asgi/oauth_app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.asgi import SlackRequestHandler
3 |
4 | app = App()
5 |
6 |
7 | @app.event("app_mention")
8 | def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | say("What's up?")
11 |
12 |
13 | api = SlackRequestHandler(app)
14 |
15 | # pip install -r requirements.txt
16 |
17 | # # -- OAuth flow -- #
18 | # export SLACK_SIGNING_SECRET=***
19 | # export SLACK_CLIENT_ID=111.111
20 | # export SLACK_CLIENT_SECRET=***
21 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
22 |
23 | # uvicorn oauth_app:api --reload --port 3000 --log-level debug
24 |
--------------------------------------------------------------------------------
/examples/asgi/requirements.txt:
--------------------------------------------------------------------------------
1 | uvicorn<1
2 | aiohttp>=3,<4
3 |
--------------------------------------------------------------------------------
/examples/async_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt.async_app import AsyncApp
6 |
7 | app = AsyncApp()
8 |
9 |
10 | @app.middleware # or app.use(log_request)
11 | async def log_request(logger, body, next):
12 | logger.debug(body)
13 | return await next()
14 |
15 |
16 | @app.event("app_mention")
17 | async def event_test(body, say, logger):
18 | logger.info(body)
19 | await say("What's up?")
20 |
21 |
22 | @app.command("/hello-bolt-python")
23 | # or app.command(re.compile(r"/hello-.+"))(test_command)
24 | async def command(ack, body):
25 | user_id = body["user_id"]
26 | await ack(f"Hi <@{user_id}>!")
27 |
28 |
29 | if __name__ == "__main__":
30 | app.start(3000)
31 |
32 | # pip install slack_bolt
33 | # export SLACK_SIGNING_SECRET=***
34 | # export SLACK_BOT_TOKEN=xoxb-***
35 | # python async_app.py
36 |
--------------------------------------------------------------------------------
/examples/aws_chalice/.chalice/config.json.oauth:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "app_name": "bolt-python-chalice",
4 | "stages": {
5 | "dev": {
6 | "api_gateway_stage": "api",
7 | "environment_variables": {
8 | "SLACK_SIGNING_SECRET": "xxx",
9 | "SLACK_CLIENT_ID": "111.111",
10 | "SLACK_CLIENT_SECRET": "xxx",
11 | "SLACK_SCOPES": "app_mentions:read,channels:history,im:history,chat:write",
12 | "SLACK_INSTALLATION_S3_BUCKET_NAME": "",
13 | "SLACK_STATE_S3_BUCKET_NAME": ""
14 | },
15 | "manage_iam_role": false,
16 | "iam_role_arn": "arn:aws:iam::1111111111111:role/bolt_python_s3_storage"
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/examples/aws_chalice/.chalice/config.json.simple:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0",
3 | "app_name": "bolt-python-chalice",
4 | "stages": {
5 | "dev": {
6 | "api_gateway_stage": "api",
7 | "environment_variables": {
8 | "SLACK_BOT_TOKEN": "xoxb-xxx",
9 | "SLACK_SIGNING_SECRET": "xxx"
10 | },
11 | "manage_iam_role": false,
12 | "iam_role_arn": "arn:aws:iam::1111111111111:role/bolt_python_lambda_invocation"
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/examples/aws_chalice/.gitignore:
--------------------------------------------------------------------------------
1 | .chalice/config.json
2 | .chalice/deployments/
3 | .chalice/venv/
4 | .chalice/deployed/
5 | vendor/
--------------------------------------------------------------------------------
/examples/aws_chalice/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # configure aws credentials properly
4 | pip install -U chalice click boto3
5 | pip install -r requirements.txt
6 | # edit .chalice/config.json
7 | rm -rf vendor && mkdir -p vendor/slack_bolt && cp -pr ../../slack_bolt/* vendor/slack_bolt/
8 | chalice deploy
9 |
--------------------------------------------------------------------------------
/examples/aws_chalice/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_sdk
--------------------------------------------------------------------------------
/examples/aws_lambda/.env.oauth_sample:
--------------------------------------------------------------------------------
1 | export SLACK_SIGNING_SECRET=
2 | export SLACK_CLIENT_ID=
3 | export SLACK_CLIENT_SECRET=
4 | export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
5 | export SLACK_INSTALLATION_S3_BUCKET_NAME=
6 | export SLACK_STATE_S3_BUCKET_NAME=
7 |
--------------------------------------------------------------------------------
/examples/aws_lambda/.env.sample:
--------------------------------------------------------------------------------
1 | export SLACK_BOT_TOKEN=
2 | export SLACK_SIGNING_SECRET=
--------------------------------------------------------------------------------
/examples/aws_lambda/.gitignore:
--------------------------------------------------------------------------------
1 | slack-bolt/
2 | slack_bolt/
3 | vendor/
4 | .env
5 |
--------------------------------------------------------------------------------
/examples/aws_lambda/aws_lambda.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt import App
4 | from slack_bolt.adapter.aws_lambda import SlackRequestHandler
5 |
6 | # process_before_response must be True when running on FaaS
7 | app = App(process_before_response=True)
8 |
9 |
10 | @app.event("app_mention")
11 | def handle_app_mentions(body, say, logger):
12 | logger.info(body)
13 | say("What's up?")
14 |
15 |
16 | @app.command("/hello-bolt-python-lambda")
17 | def respond_to_slack_within_3_seconds(ack):
18 | ack("Thanks!")
19 |
20 |
21 | SlackRequestHandler.clear_all_log_handlers()
22 | logging.basicConfig(format="%(asctime)s %(message)s", level=logging.DEBUG)
23 |
24 |
25 | def handler(event, context):
26 | slack_handler = SlackRequestHandler(app=app)
27 | return slack_handler.handle(event, context)
28 |
29 |
30 | # export SLACK_SIGNING_SECRET=***
31 | # export SLACK_BOT_TOKEN=xoxb-***
32 |
33 | # rm -rf vendor && cp -pr ../../src/* vendor/
34 | # pip install git+https://github.com/nficano/python-lambda
35 | # lambda deploy --config-file aws_lambda_config.yaml --requirements requirements.txt
36 |
--------------------------------------------------------------------------------
/examples/aws_lambda/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rm -rf vendor && mkdir -p vendor/slack_bolt && cp -pr ../../slack_bolt/* vendor/slack_bolt/
3 | pip install git+https://github.com/nficano/python-lambda
4 | lambda deploy \
5 | --config-file aws_lambda_config.yaml \
6 | --requirements requirements.txt
7 |
--------------------------------------------------------------------------------
/examples/aws_lambda/deploy_lazy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rm -rf slack_bolt && mkdir slack_bolt && cp -pr ../../slack_bolt/* slack_bolt/
3 | pip install git+https://github.com/nficano/python-lambda
4 | lambda deploy \
5 | --config-file lazy_aws_lambda_config.yaml \
6 | --requirements requirements.txt
7 |
--------------------------------------------------------------------------------
/examples/aws_lambda/deploy_oauth.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rm -rf slack_bolt && mkdir slack_bolt && cp -pr ../../slack_bolt/* slack_bolt/
3 | pip install git+https://github.com/nficano/python-lambda
4 | lambda deploy \
5 | --config-file aws_lambda_oauth_config.yaml \
6 | --requirements requirements_oauth.txt
7 |
--------------------------------------------------------------------------------
/examples/aws_lambda/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_sdk
--------------------------------------------------------------------------------
/examples/aws_lambda/requirements_oauth.txt:
--------------------------------------------------------------------------------
1 | slack_sdk
--------------------------------------------------------------------------------
/examples/bottle/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 | from slack_bolt.adapter.bottle import SlackRequestHandler
7 |
8 | app = App()
9 |
10 |
11 | @app.middleware # or app.use(log_request)
12 | def log_request(logger, body, next):
13 | logger.debug(body)
14 | return next()
15 |
16 |
17 | @app.event("app_mention")
18 | def event_test(ack, body, say, logger):
19 | logger.info(body)
20 | say("What's up?")
21 |
22 |
23 | from bottle import post, request, response, run
24 |
25 | handler = SlackRequestHandler(app)
26 |
27 |
28 | @post("/slack/events")
29 | def slack_events():
30 | return handler.handle(request, response)
31 |
32 |
33 | if __name__ == "__main__":
34 | run(host="0.0.0.0", port=3000, reloader=True)
35 |
36 | # pip install -r requirements.txt
37 | # export SLACK_SIGNING_SECRET=***
38 | # export SLACK_BOT_TOKEN=xoxb-***
39 | # python app.py
40 |
--------------------------------------------------------------------------------
/examples/bottle/requirements.txt:
--------------------------------------------------------------------------------
1 | bottle>=0.12,<1
--------------------------------------------------------------------------------
/examples/cherrypy/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 | from slack_bolt.adapter.cherrypy import SlackRequestHandler
7 |
8 | app = App()
9 |
10 |
11 | @app.middleware # or app.use(log_request)
12 | def log_request(logger, body, next):
13 | logger.debug(body)
14 | return next()
15 |
16 |
17 | @app.command("/hello-bolt-python")
18 | def hello_command(ack):
19 | ack("Hi from CherryPy")
20 |
21 |
22 | @app.event("app_mention")
23 | def event_test(body, say, logger):
24 | logger.info(body)
25 | say("What's up?")
26 |
27 |
28 | import cherrypy
29 |
30 | handler = SlackRequestHandler(app)
31 |
32 |
33 | class SlackApp(object):
34 | @cherrypy.expose
35 | @cherrypy.tools.slack_in()
36 | def events(self, **kwargs):
37 | return handler.handle()
38 |
39 |
40 | if __name__ == "__main__":
41 | cherrypy.config.update({"server.socket_port": 3000})
42 | cherrypy.quickstart(SlackApp(), "/slack")
43 |
44 | # pip install -r requirements.txt
45 | # export SLACK_SIGNING_SECRET=***
46 | # export SLACK_BOT_TOKEN=xoxb-***
47 | # python app.py
48 |
--------------------------------------------------------------------------------
/examples/cherrypy/requirements.txt:
--------------------------------------------------------------------------------
1 | CherryPy>=18,<19
2 |
--------------------------------------------------------------------------------
/examples/django/.gitignore:
--------------------------------------------------------------------------------
1 | db.sqlite3
2 | db/
3 |
--------------------------------------------------------------------------------
/examples/django/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Django's command-line utility for administrative tasks."""
3 | import os
4 | import sys
5 |
6 |
7 | def main():
8 | """Run administrative tasks."""
9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myslackapp.settings")
10 | try:
11 | from django.core.management import execute_from_command_line
12 | except ImportError as exc:
13 | raise ImportError(
14 | "Couldn't import Django. Are you sure it's installed and "
15 | "available on your PYTHONPATH environment variable? Did you "
16 | "forget to activate a virtual environment?"
17 | ) from exc
18 | execute_from_command_line(sys.argv)
19 |
20 |
21 | if __name__ == "__main__":
22 | main()
23 |
--------------------------------------------------------------------------------
/examples/django/myslackapp/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/myslackapp/__init__.py
--------------------------------------------------------------------------------
/examples/django/myslackapp/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for myslackapp project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myslackapp.settings")
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/examples/django/myslackapp/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for myslackapp project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myslackapp.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/examples/django/mysql-docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.9'
2 | services:
3 | db:
4 | image: mysql:8
5 | environment:
6 | MYSQL_DATABASE: slackapp
7 | MYSQL_USER: app
8 | MYSQL_PASSWORD: password
9 | MYSQL_ROOT_PASSWORD: password
10 | #command:
11 | # - '--wait_timeout=3'
12 | volumes:
13 | - './db:/var/lib/mysql'
14 | ports:
15 | - 33306:3306
16 |
17 |
--------------------------------------------------------------------------------
/examples/django/oauth_app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/oauth_app/__init__.py
--------------------------------------------------------------------------------
/examples/django/oauth_app/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class OauthAppConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "oauth_app"
7 |
--------------------------------------------------------------------------------
/examples/django/oauth_app/migrations/0002_token_rotation.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.3 on 2021-07-15 23:44
2 |
3 | from django.db import migrations
4 | from django.db import models
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ("oauth_app", "0001_initial"),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField("SlackBot", "bot_refresh_token", models.TextField(null=True)),
15 | migrations.AddField("SlackBot", "bot_token_expires_at", models.DateTimeField(null=True)),
16 | migrations.AddField("SlackInstallation", "bot_refresh_token", models.TextField(null=True)),
17 | migrations.AddField("SlackInstallation", "bot_token_expires_at", models.DateTimeField(null=True)),
18 | migrations.AddField("SlackInstallation", "user_refresh_token", models.TextField(null=True)),
19 | migrations.AddField(
20 | "SlackInstallation",
21 | "user_token_expires_at",
22 | models.DateTimeField(null=True),
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/examples/django/oauth_app/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/oauth_app/migrations/__init__.py
--------------------------------------------------------------------------------
/examples/django/oauth_app/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from django.http import HttpRequest
4 | from django.views.decorators.csrf import csrf_exempt
5 |
6 | from slack_bolt.adapter.django import SlackRequestHandler
7 | from .slack_listeners import app
8 |
9 | handler = SlackRequestHandler(app=app)
10 |
11 |
12 | @csrf_exempt
13 | def slack_events_handler(request: HttpRequest):
14 | return handler.handle(request)
15 |
16 |
17 | def slack_oauth_handler(request: HttpRequest):
18 | return handler.handle(request)
19 |
20 |
21 | urlpatterns = [
22 | path("slack/events", slack_events_handler, name="handle"),
23 | path("slack/install", slack_oauth_handler, name="install"),
24 | path("slack/oauth_redirect", slack_oauth_handler, name="oauth_redirect"),
25 | ]
26 |
--------------------------------------------------------------------------------
/examples/django/requirements.txt:
--------------------------------------------------------------------------------
1 | Django>=3.2,<4
2 | slack-bolt>=1.7,<2
3 |
--------------------------------------------------------------------------------
/examples/django/simple_app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/simple_app/__init__.py
--------------------------------------------------------------------------------
/examples/django/simple_app/apps.py:
--------------------------------------------------------------------------------
1 | from django.apps import AppConfig
2 |
3 |
4 | class SimpleAppConfig(AppConfig):
5 | default_auto_field = "django.db.models.BigAutoField"
6 | name = "simple_app"
7 |
--------------------------------------------------------------------------------
/examples/django/simple_app/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/examples/django/simple_app/migrations/__init__.py
--------------------------------------------------------------------------------
/examples/django/simple_app/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models # noqa: F401
2 |
3 | # Create your models here.
4 |
--------------------------------------------------------------------------------
/examples/django/simple_app/slack_listeners.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from slack_bolt import App
5 |
6 | logger = logging.getLogger(__name__)
7 |
8 | app = App(
9 | token=os.environ["SLACK_BOT_TOKEN"],
10 | signing_secret=os.environ["SLACK_SIGNING_SECRET"],
11 | # disable eagerly verifying the given SLACK_BOT_TOKEN value
12 | token_verification_enabled=False,
13 | )
14 |
15 |
16 | @app.event("app_mention")
17 | def handle_app_mentions(logger, event, say):
18 | logger.info(event)
19 | say(f"Hi there, <@{event['user']}>")
20 |
--------------------------------------------------------------------------------
/examples/django/simple_app/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from slack_bolt.adapter.django import SlackRequestHandler
4 | from .slack_listeners import app
5 |
6 | handler = SlackRequestHandler(app=app)
7 |
8 | from django.http import HttpRequest
9 | from django.views.decorators.csrf import csrf_exempt
10 |
11 |
12 | @csrf_exempt
13 | def slack_events_handler(request: HttpRequest):
14 | return handler.handle(request)
15 |
16 |
17 | urlpatterns = [
18 | path("slack/events", slack_events_handler, name="slack_events"),
19 | ]
20 |
--------------------------------------------------------------------------------
/examples/docker/aiohttp/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # docker build . -t your-repo/hello-bolt
3 | #
4 | FROM python:3.8.5-slim-buster as builder
5 | RUN apt-get update && apt-get clean
6 | COPY requirements.txt /build/
7 | WORKDIR /build/
8 | RUN pip install -U pip && pip install -r requirements.txt
9 |
10 | FROM python:3.8.5-slim-buster as app
11 | COPY --from=builder /build/ /app/
12 | COPY --from=builder /usr/local/lib/ /usr/local/lib/
13 | WORKDIR /app/
14 | COPY *.py /app/
15 | ENTRYPOINT python main.py
16 |
17 | #
18 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt
19 | #
--------------------------------------------------------------------------------
/examples/docker/aiohttp/main.py:
--------------------------------------------------------------------------------
1 | import os
2 | import logging
3 |
4 | from slack_bolt.async_app import AsyncApp
5 |
6 | logging.basicConfig(level=logging.DEBUG)
7 | app = AsyncApp()
8 |
9 |
10 | @app.command("/hello-bolt-python")
11 | async def hello(body, ack):
12 | user_id = body["user_id"]
13 | await ack(f"Hi <@{user_id}>!")
14 |
15 |
16 | if __name__ == "__main__":
17 | app.start(port=int(os.environ.get("PORT", int(os.environ.get("PORT", 3000)))))
18 |
--------------------------------------------------------------------------------
/examples/docker/aiohttp/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | aiohttp>=3,<4
--------------------------------------------------------------------------------
/examples/docker/asgi/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # docker build . -t your-repo/hello-bolt
3 | #
4 | FROM python:3.11-alpine3.17 as builder
5 | COPY requirements.txt /build/
6 | WORKDIR /build/
7 | RUN pip install -U pip && pip install -r requirements.txt
8 |
9 | FROM python:3.11-alpine3.17 as app
10 | WORKDIR /app/
11 | COPY --from=builder /usr/local/bin/ /usr/local/bin/
12 | COPY --from=builder /usr/local/lib/ /usr/local/lib/
13 | COPY main.py /app/
14 | ENTRYPOINT uvicorn main:asgi_app --port $PORT --workers 1
15 |
16 | #
17 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt
18 | #
19 |
--------------------------------------------------------------------------------
/examples/docker/asgi/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from slack_bolt import App
3 | from slack_bolt.adapter.asgi import SlackRequestHandler
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = App()
7 |
8 |
9 | @app.event("app_mention")
10 | def handle_app_mentions(body, say, logger):
11 | logger.info(body)
12 | say("What's up?")
13 |
14 |
15 | asgi_app = SlackRequestHandler(app)
16 |
--------------------------------------------------------------------------------
/examples/docker/asgi/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | uvicorn<1
3 |
--------------------------------------------------------------------------------
/examples/docker/fastapi-gunicorn/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # docker build . -t your-repo/hello-bolt
3 | #
4 | FROM tiangolo/uvicorn-gunicorn:python3.8-slim
5 | WORKDIR /app/
6 | COPY *.py /app/
7 | COPY requirements.txt /app/
8 | RUN pip install -U pip && pip install -r requirements.txt
9 |
10 | #
11 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e VARIABLE_NAME="api" -p 80:80 -it your-repo/hello-bolt
12 | # or
13 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e VARIABLE_NAME="api" -p 3000:80 -it your-repo/hello-bolt
14 |
--------------------------------------------------------------------------------
/examples/docker/fastapi-gunicorn/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt.async_app import AsyncApp
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = AsyncApp()
7 |
8 |
9 | @app.command("/hello-bolt-python")
10 | async def hello(body, ack):
11 | user_id = body["user_id"]
12 | await ack(f"Hi <@{user_id}>!")
13 |
14 |
15 | from fastapi import FastAPI, Request
16 |
17 | api = FastAPI()
18 |
19 | from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
20 |
21 | app_handler = AsyncSlackRequestHandler(app)
22 |
23 |
24 | @api.post("/slack/events")
25 | async def endpoint(req: Request):
26 | return await app_handler.handle(req)
27 |
--------------------------------------------------------------------------------
/examples/docker/fastapi-gunicorn/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | fastapi<1
3 | aiohttp>=3,<4
--------------------------------------------------------------------------------
/examples/docker/flask-gunicorn/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # docker build . -t your-repo/hello-bolt
3 | #
4 | FROM python:3.8.5-slim-buster as builder
5 | COPY requirements.txt /build/
6 | WORKDIR /build/
7 | RUN pip install -U pip && pip install -r requirements.txt
8 |
9 | FROM python:3.8.5-slim-buster as app
10 | WORKDIR /app/
11 | COPY --from=builder /usr/local/bin/ /usr/local/bin/
12 | COPY --from=builder /usr/local/lib/ /usr/local/lib/
13 | COPY *.py /app/
14 | ENTRYPOINT gunicorn --bind :$PORT --workers 1 --threads 2 --timeout 0 main:flask_app
15 |
16 | #
17 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt
18 | #
--------------------------------------------------------------------------------
/examples/docker/flask-gunicorn/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt import App
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = App()
7 |
8 |
9 | @app.command("/hello-bolt-python")
10 | def hello(body, ack):
11 | user_id = body["user_id"]
12 | ack(f"Hi <@{user_id}>!")
13 |
14 |
15 | from flask import Flask, request
16 | from slack_bolt.adapter.flask import SlackRequestHandler
17 |
18 | flask_app = Flask(__name__)
19 | handler = SlackRequestHandler(app)
20 |
21 |
22 | @flask_app.route("/slack/events", methods=["POST"])
23 | def slack_events():
24 | return handler.handle(request)
25 |
--------------------------------------------------------------------------------
/examples/docker/flask-gunicorn/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | Flask>=1.1
3 | gunicorn>=20
--------------------------------------------------------------------------------
/examples/docker/flask-uwsgi/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # docker build . -t your-repo/hello-bolt
3 | #
4 | FROM python:3.8.5-slim-buster as builder
5 | RUN apt-get update \
6 | && apt-get -y install build-essential libpcre3-dev \
7 | && apt-get clean
8 | COPY requirements.txt /build/
9 | WORKDIR /build/
10 | RUN pip install -U pip && pip install -r requirements.txt
11 |
12 | FROM python:3.8.5-slim-buster as app
13 | WORKDIR /app/
14 | COPY --from=builder /usr/local/bin/ /usr/local/bin/
15 | COPY --from=builder /usr/local/lib/ /usr/local/lib/
16 | COPY *.py /app/
17 | COPY uwsgi.ini /app/
18 | ENTRYPOINT uwsgi --ini uwsgi.ini --http :$PORT
19 |
20 | #
21 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt
22 | #
--------------------------------------------------------------------------------
/examples/docker/flask-uwsgi/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt import App
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = App()
7 |
8 |
9 | @app.command("/hello-bolt-python")
10 | def hello(body, ack):
11 | user_id = body["user_id"]
12 | ack(f"Hi <@{user_id}>!")
13 |
14 |
15 | from flask import Flask, request
16 | from slack_bolt.adapter.flask import SlackRequestHandler
17 |
18 | flask_app = Flask(__name__)
19 | handler = SlackRequestHandler(app)
20 |
21 |
22 | @flask_app.route("/slack/events", methods=["POST"])
23 | def slack_events():
24 | return handler.handle(request)
25 |
--------------------------------------------------------------------------------
/examples/docker/flask-uwsgi/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | Flask>1
3 | uWSGI>=2,<3
--------------------------------------------------------------------------------
/examples/docker/flask-uwsgi/uwsgi.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | module = main:flask_app
3 | master = true
4 | uid = nobody
5 | socket = /tmp/uwsgi.sock
6 | die-on-term = true
7 | enable-threads = true
--------------------------------------------------------------------------------
/examples/docker/sanic/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # docker build . -t your-repo/hello-bolt
3 | #
4 | FROM python:3.8.5-slim-buster as builder
5 | COPY requirements.txt /build/
6 | WORKDIR /build/
7 | RUN pip install -U pip && pip install -r requirements.txt
8 |
9 | FROM python:3.8.5-slim-buster as app
10 | WORKDIR /app/
11 | COPY *.py /app/
12 | COPY --from=builder /usr/local/bin/ /usr/local/bin/
13 | COPY --from=builder /usr/local/lib/ /usr/local/lib/
14 | ENTRYPOINT python main.py
15 |
16 | #
17 | # docker run -e SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET -e SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN -e PORT=3000 -p 3000:3000 -it your-repo/hello-bolt
18 | #
19 |
--------------------------------------------------------------------------------
/examples/docker/sanic/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from slack_bolt.async_app import AsyncApp
5 | from slack_bolt.adapter.sanic import AsyncSlackRequestHandler
6 |
7 | logging.basicConfig(level=logging.DEBUG)
8 | app = AsyncApp()
9 | app_handler = AsyncSlackRequestHandler(app)
10 |
11 |
12 | @app.command("/hello-bolt-python")
13 | async def hello(body, ack):
14 | user_id = body["user_id"]
15 | await ack(f"Hi <@{user_id}>!")
16 |
17 |
18 | from sanic import Sanic
19 | from sanic.request import Request
20 |
21 | api = Sanic(name="awesome-slack-app")
22 |
23 |
24 | @api.post("/slack/events")
25 | async def endpoint(req: Request):
26 | return await app_handler.handle(req)
27 |
28 |
29 | if __name__ == "__main__":
30 | api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000)))
31 |
--------------------------------------------------------------------------------
/examples/docker/sanic/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | aiohttp>=3,<4
3 | sanic>=20,<21
--------------------------------------------------------------------------------
/examples/events_app.py:
--------------------------------------------------------------------------------
1 | import re
2 | import logging
3 |
4 | logging.basicConfig(level=logging.DEBUG)
5 |
6 | from slack_bolt import App
7 |
8 | app = App()
9 |
10 |
11 | @app.middleware # or app.use(log_request)
12 | def log_request(logger, body, next):
13 | logger.debug(body)
14 | return next()
15 |
16 |
17 | @app.event("app_mention")
18 | def event_test(body, say, logger):
19 | logger.info(body)
20 | say("What's up?")
21 |
22 |
23 | @app.event("reaction_added")
24 | def say_something_to_reaction(say):
25 | say("OK!")
26 |
27 |
28 | @app.message("test")
29 | def test_message(logger, body):
30 | logger.info(body)
31 |
32 |
33 | @app.message(re.compile("bug"))
34 | def mention_bug(logger, body):
35 | logger.info(body)
36 |
37 |
38 | @app.event("message")
39 | def ack_the_rest_of_message_events(logger, body):
40 | logger.info(body)
41 |
42 |
43 | if __name__ == "__main__":
44 | app.start(3000)
45 |
46 | # pip install slack_bolt
47 | # export SLACK_SIGNING_SECRET=***
48 | # export SLACK_BOT_TOKEN=xoxb-***
49 | # python events_app.py
50 |
--------------------------------------------------------------------------------
/examples/falcon/requirements.txt:
--------------------------------------------------------------------------------
1 | falcon>=2,<3
2 | gunicorn>=20,<21
3 | uvicorn
4 |
--------------------------------------------------------------------------------
/examples/fastapi/app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.fastapi import SlackRequestHandler
3 |
4 | app = App()
5 | app_handler = SlackRequestHandler(app)
6 |
7 |
8 | @app.event("app_mention")
9 | def handle_app_mentions(body, say, logger):
10 | logger.info(body)
11 | say("What's up?")
12 |
13 |
14 | @app.event("message")
15 | def handle_message():
16 | pass
17 |
18 |
19 | from fastapi import FastAPI, Request
20 |
21 | api = FastAPI()
22 |
23 |
24 | @api.post("/slack/events")
25 | async def endpoint(req: Request):
26 | return await app_handler.handle(req)
27 |
28 |
29 | # pip install -r requirements.txt
30 | # export SLACK_SIGNING_SECRET=***
31 | # export SLACK_BOT_TOKEN=xoxb-***
32 | # uvicorn app:api --reload --port 3000 --log-level warning
33 |
--------------------------------------------------------------------------------
/examples/fastapi/async_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt.async_app import AsyncApp
6 | from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
7 |
8 | app = AsyncApp()
9 | app_handler = AsyncSlackRequestHandler(app)
10 |
11 |
12 | @app.event("app_mention")
13 | async def handle_app_mentions(body, say, logger):
14 | logger.info(body)
15 | await say("What's up?")
16 |
17 |
18 | @app.event("message")
19 | async def handle_message():
20 | pass
21 |
22 |
23 | from fastapi import FastAPI, Request
24 |
25 | api = FastAPI()
26 |
27 |
28 | @api.post("/slack/events")
29 | async def endpoint(req: Request):
30 | return await app_handler.handle(req)
31 |
32 |
33 | # pip install -r requirements.txt
34 | # export SLACK_SIGNING_SECRET=***
35 | # export SLACK_BOT_TOKEN=xoxb-***
36 | # uvicorn async_app:api --reload --port 3000 --log-level warning
37 |
--------------------------------------------------------------------------------
/examples/fastapi/async_app_custom_props.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt.async_app import AsyncApp
6 | from slack_bolt.adapter.fastapi.async_handler import AsyncSlackRequestHandler
7 |
8 | app = AsyncApp()
9 | app_handler = AsyncSlackRequestHandler(app)
10 |
11 |
12 | @app.event("app_mention")
13 | async def handle_app_mentions(context, say, logger):
14 | logger.info(context)
15 | assert context.get("foo") == "FOO"
16 | await say("What's up?")
17 |
18 |
19 | @app.event("message")
20 | async def handle_message():
21 | pass
22 |
23 |
24 | from fastapi import FastAPI, Request, Depends
25 |
26 | api = FastAPI()
27 |
28 |
29 | def get_foo():
30 | yield "FOO"
31 |
32 |
33 | @api.post("/slack/events")
34 | async def endpoint(req: Request, foo: str = Depends(get_foo)):
35 | return await app_handler.handle(req, {"foo": foo})
36 |
37 |
38 | # pip install -r requirements.txt
39 | # export SLACK_SIGNING_SECRET=***
40 | # export SLACK_BOT_TOKEN=xoxb-***
41 | # uvicorn async_app_custom_props:api --reload --port 3000 --log-level warning
42 |
--------------------------------------------------------------------------------
/examples/fastapi/oauth_app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.fastapi import SlackRequestHandler
3 |
4 | app = App()
5 | app_handler = SlackRequestHandler(app)
6 |
7 |
8 | @app.event("app_mention")
9 | def handle_app_mentions(body, say, logger):
10 | logger.info(body)
11 | say("What's up?")
12 |
13 |
14 | @app.event("message")
15 | def handle_message():
16 | pass
17 |
18 |
19 | from fastapi import FastAPI, Request
20 |
21 | api = FastAPI()
22 |
23 |
24 | @api.post("/slack/events")
25 | async def endpoint(req: Request):
26 | return await app_handler.handle(req)
27 |
28 |
29 | @api.get("/slack/install")
30 | async def install(req: Request):
31 | return await app_handler.handle(req)
32 |
33 |
34 | @api.get("/slack/oauth_redirect")
35 | async def oauth_redirect(req: Request):
36 | return await app_handler.handle(req)
37 |
38 |
39 | # pip install -r requirements.txt
40 |
41 | # # -- OAuth flow -- #
42 | # export SLACK_SIGNING_SECRET=***
43 | # export SLACK_CLIENT_ID=111.111
44 | # export SLACK_CLIENT_SECRET=***
45 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
46 |
47 | # uvicorn oauth_app:api --reload --port 3000 --log-level warning
48 |
--------------------------------------------------------------------------------
/examples/fastapi/requirements.txt:
--------------------------------------------------------------------------------
1 | fastapi<1
2 | uvicorn<1
3 |
--------------------------------------------------------------------------------
/examples/flask/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 | from slack_bolt.adapter.flask import SlackRequestHandler
7 |
8 | app = App()
9 |
10 |
11 | @app.middleware # or app.use(log_request)
12 | def log_request(logger, body, next):
13 | logger.debug(body)
14 | return next()
15 |
16 |
17 | @app.event("app_mention")
18 | def event_test(body, say, logger):
19 | logger.info(body)
20 | say("What's up?")
21 |
22 |
23 | @app.event("message")
24 | def handle_message():
25 | pass
26 |
27 |
28 | from flask import Flask, request
29 |
30 | flask_app = Flask(__name__)
31 | handler = SlackRequestHandler(app)
32 |
33 |
34 | @flask_app.route("/slack/events", methods=["POST"])
35 | def slack_events():
36 | return handler.handle(request)
37 |
38 |
39 | # pip install -r requirements.txt
40 | # export SLACK_SIGNING_SECRET=***
41 | # export SLACK_BOT_TOKEN=xoxb-***
42 | # FLASK_APP=app.py FLASK_ENV=development flask run -p 3000
43 |
--------------------------------------------------------------------------------
/examples/flask/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask>1
2 |
--------------------------------------------------------------------------------
/examples/getting_started/.gitignore:
--------------------------------------------------------------------------------
1 | # Python
2 | .venv/
3 | env*/
--------------------------------------------------------------------------------
/examples/getting_started/requirements.txt:
--------------------------------------------------------------------------------
1 | slack-bolt
--------------------------------------------------------------------------------
/examples/google_app_engine/flask/.gcloudignore:
--------------------------------------------------------------------------------
1 | # This file specifies files that are *not* uploaded to Google Cloud Platform
2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of
3 | # "#!include" directives (which insert the entries of the given .gitignore-style
4 | # file at that point).
5 | #
6 | # For more information, run:
7 | # $ gcloud topic gcloudignore
8 | #
9 | .gcloudignore
10 | # If you would like to upload your .git directory, .gitignore file or files
11 | # from your .gitignore file, remove the corresponding line
12 | # below:
13 | .git
14 | .gitignore
15 |
16 | # Python pycache:
17 | __pycache__/
18 | # Ignored by the build system
19 | /setup.cfg
--------------------------------------------------------------------------------
/examples/google_app_engine/flask/.gitignore:
--------------------------------------------------------------------------------
1 | env_variables.yaml
--------------------------------------------------------------------------------
/examples/google_app_engine/flask/app.yaml:
--------------------------------------------------------------------------------
1 | runtime: python38
2 |
3 | inbound_services:
4 | - warmup
5 |
6 | automatic_scaling:
7 | min_idle_instances: 1
8 |
9 |
10 | handlers:
11 | - url: /slack/events
12 | secure: always
13 | script: auto
14 |
15 | includes:
16 | - env_variables.yaml
17 |
--------------------------------------------------------------------------------
/examples/google_app_engine/flask/env_variables.yaml.sample:
--------------------------------------------------------------------------------
1 | env_variables:
2 | SLACK_BOT_TOKEN: "xoxb-xxx"
3 | SLACK_SIGNING_SECRET: "yyy"
--------------------------------------------------------------------------------
/examples/google_app_engine/flask/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from slack_bolt import App
5 |
6 | logging.basicConfig(level=logging.DEBUG)
7 | bolt_app = App()
8 |
9 |
10 | @bolt_app.command("/hey-google-app-engine")
11 | def hello(body, ack):
12 | user_id = body["user_id"]
13 | ack(f"Hi <@{user_id}>!")
14 |
15 |
16 | from flask import Flask, request
17 | from slack_bolt.adapter.flask import SlackRequestHandler
18 |
19 | app = Flask(__name__)
20 | handler = SlackRequestHandler(bolt_app)
21 |
22 |
23 | @app.route("/_ah/warmup")
24 | def warmup():
25 | # Handle your warmup logic here, e.g. set up a database connection pool
26 | return "", 200, {}
27 |
28 |
29 | @app.route("/slack/events", methods=["POST"])
30 | def slack_events():
31 | return handler.handle(request)
32 |
33 |
34 | # Only for local debug
35 | if __name__ == "__main__":
36 | app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 3000)))
37 |
--------------------------------------------------------------------------------
/examples/google_app_engine/flask/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | Flask>=1.1,<2
--------------------------------------------------------------------------------
/examples/google_cloud_functions/.env.yaml.oauth-sample:
--------------------------------------------------------------------------------
1 | SLACK_CLIENT_ID: '1111.222'
2 | SLACK_CLIENT_SECRET: 'xxx'
3 | SLACK_SIGNING_SECRET: 'yyy'
4 | SLACK_SCOPES: 'app_mentions:read,chat:write,commands'
5 |
--------------------------------------------------------------------------------
/examples/google_cloud_functions/.env.yaml.sample:
--------------------------------------------------------------------------------
1 | SLACK_SIGNING_SECRET: xxx
2 | SLACK_BOT_TOKEN: xoxb-xxx
--------------------------------------------------------------------------------
/examples/google_cloud_functions/.gcloudignore:
--------------------------------------------------------------------------------
1 | # This file specifies files that are *not* uploaded to Google Cloud Platform
2 | # using gcloud. It follows the same syntax as .gitignore, with the addition of
3 | # "#!include" directives (which insert the entries of the given .gitignore-style
4 | # file at that point).
5 | #
6 | # For more information, run:
7 | # $ gcloud topic gcloudignore
8 | #
9 | .gcloudignore
10 | # If you would like to upload your .git directory, .gitignore file or files
11 | # from your .gitignore file, remove the corresponding line
12 | # below:
13 | .git
14 | .gitignore
15 | .env.yaml.sample
16 |
17 | node_modules
18 | #!include:.gitignore
19 |
--------------------------------------------------------------------------------
/examples/google_cloud_functions/.gitignore:
--------------------------------------------------------------------------------
1 | .env.yaml
2 | main.py
3 |
--------------------------------------------------------------------------------
/examples/google_cloud_functions/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask>1
2 | slack_bolt
3 | google-cloud-datastore>=2.1.0,<3
--------------------------------------------------------------------------------
/examples/google_cloud_run/aiohttp/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy
3 | #
4 | # export PROJECT_ID=`gcloud config get-value project`
5 | # export SLACK_SIGNING_SECRET=
6 | # export SLACK_BOT_TOKEN=
7 | # gcloud builds submit --tag gcr.io/$PROJECT_ID/helloworld
8 | # gcloud run deploy helloworld --image gcr.io/$PROJECT_ID/helloworld --platform managed --update-env-vars SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN
9 | #
10 |
11 | # ----------------------------------------------
12 | # Use the official lightweight Python image.
13 | # https://hub.docker.com/_/python
14 | FROM python:3.8.5-slim-buster
15 |
16 | # Allow statements and log messages to immediately appear in the Knative logs
17 | ENV PYTHONUNBUFFERED True
18 |
19 | # Copy local code to the container image.
20 | ENV APP_HOME /app
21 | WORKDIR $APP_HOME
22 | COPY . ./
23 |
24 | # Install production dependencies.
25 | RUN pip install -U pip && pip install -r requirements.txt
26 |
27 | # Start AIOHTTP server
28 | ENTRYPOINT python main.py
29 |
--------------------------------------------------------------------------------
/examples/google_cloud_run/aiohttp/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from slack_bolt.async_app import AsyncApp
5 |
6 | logging.basicConfig(level=logging.DEBUG)
7 | app = AsyncApp()
8 |
9 |
10 | @app.command("/hey-google")
11 | async def hello(body, ack):
12 | user_id = body["user_id"]
13 | await ack(f"Hi <@{user_id}>!")
14 |
15 |
16 | if __name__ == "__main__":
17 | app.start(port=int(os.environ.get("PORT", 3000)))
18 |
--------------------------------------------------------------------------------
/examples/google_cloud_run/aiohttp/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | aiohttp>=3,<4
--------------------------------------------------------------------------------
/examples/google_cloud_run/flask-gunicorn/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy
3 | #
4 | # export PROJECT_ID=`gcloud config get-value project`
5 | # export SLACK_SIGNING_SECRET=
6 | # export SLACK_BOT_TOKEN=
7 | # gcloud builds submit --tag gcr.io/$PROJECT_ID/helloworld
8 | # gcloud run deploy helloworld --image gcr.io/$PROJECT_ID/helloworld --platform managed --update-env-vars SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN
9 | #
10 |
11 | # ----------------------------------------------
12 | # Use the official lightweight Python image.
13 | # https://hub.docker.com/_/python
14 | FROM python:3.8.5-slim-buster
15 |
16 | # Allow statements and log messages to immediately appear in the Knative logs
17 | ENV PYTHONUNBUFFERED True
18 |
19 | # Copy local code to the container image.
20 | ENV APP_HOME /app
21 | WORKDIR $APP_HOME
22 | COPY . ./
23 |
24 | # Install production dependencies.
25 | RUN pip install -U pip && pip install -r requirements.txt
26 |
27 | # Run the web service on container startup.
28 | ENTRYPOINT gunicorn --bind :$PORT --workers 1 --threads 2 --timeout 0 main:flask_app
29 |
--------------------------------------------------------------------------------
/examples/google_cloud_run/flask-gunicorn/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from slack_bolt import App
5 |
6 | logging.basicConfig(level=logging.DEBUG)
7 | app = App()
8 |
9 |
10 | @app.command("/hey-google")
11 | def hello(body, ack):
12 | user_id = body["user_id"]
13 | ack(f"Hi <@{user_id}>!")
14 |
15 |
16 | from flask import Flask, request
17 | from slack_bolt.adapter.flask import SlackRequestHandler
18 |
19 | flask_app = Flask(__name__)
20 | handler = SlackRequestHandler(app)
21 |
22 |
23 | @flask_app.route("/slack/events", methods=["POST"])
24 | def slack_events():
25 | return handler.handle(request)
26 |
27 |
28 | # Only for local debug
29 | if __name__ == "__main__":
30 | flask_app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 3000)))
31 |
--------------------------------------------------------------------------------
/examples/google_cloud_run/flask-gunicorn/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | Flask>=1.1
3 | gunicorn>=20
--------------------------------------------------------------------------------
/examples/google_cloud_run/sanic/Dockerfile:
--------------------------------------------------------------------------------
1 | #
2 | # https://cloud.google.com/run/docs/quickstarts/build-and-deploy
3 | #
4 | # export PROJECT_ID=`gcloud config get-value project`
5 | # export SLACK_SIGNING_SECRET=
6 | # export SLACK_BOT_TOKEN=
7 | # gcloud builds submit --tag gcr.io/$PROJECT_ID/helloworld
8 | # gcloud run deploy helloworld --image gcr.io/$PROJECT_ID/helloworld --platform managed --update-env-vars SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN
9 | #
10 |
11 | # ----------------------------------------------
12 | # Use the official lightweight Python image.
13 | # https://hub.docker.com/_/python
14 | FROM python:3.8.5-slim-buster
15 |
16 | # Allow statements and log messages to immediately appear in the Knative logs
17 | ENV PYTHONUNBUFFERED True
18 |
19 | # Copy local code to the container image.
20 | ENV APP_HOME /app
21 | WORKDIR $APP_HOME
22 | COPY . ./
23 |
24 | # Install production dependencies.
25 | RUN pip install -U pip && pip install -r requirements.txt
26 |
27 | # Start Sanic server
28 | ENTRYPOINT python main.py
29 |
--------------------------------------------------------------------------------
/examples/google_cloud_run/sanic/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import os
3 |
4 | from slack_bolt.async_app import AsyncApp
5 | from slack_bolt.adapter.sanic import AsyncSlackRequestHandler
6 |
7 | logging.basicConfig(level=logging.DEBUG)
8 | app = AsyncApp()
9 | app_handler = AsyncSlackRequestHandler(app)
10 |
11 |
12 | @app.command("/hey-google")
13 | async def hello(body, ack):
14 | user_id = body["user_id"]
15 | await ack(f"Hi <@{user_id}>!")
16 |
17 |
18 | from sanic import Sanic
19 | from sanic.request import Request
20 |
21 | api = Sanic(name="awesome-slack-app")
22 |
23 |
24 | @api.post("/slack/events")
25 | async def endpoint(req: Request):
26 | return await app_handler.handle(req)
27 |
28 |
29 | if __name__ == "__main__":
30 | api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000)))
31 |
--------------------------------------------------------------------------------
/examples/google_cloud_run/sanic/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | aiohttp>=3,<4
3 | sanic>=20,<21
--------------------------------------------------------------------------------
/examples/heroku/Procfile:
--------------------------------------------------------------------------------
1 | web: gunicorn --bind :$PORT --workers 1 --threads 10 --timeout 0 main:flask_app
2 |
--------------------------------------------------------------------------------
/examples/heroku/main.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt import App
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = App()
7 |
8 |
9 | @app.command("/hello-bolt-python-heroku")
10 | def hello(body, ack):
11 | user_id = body["user_id"]
12 | ack(f"Hi <@{user_id}>!")
13 |
14 |
15 | from flask import Flask, request
16 | from slack_bolt.adapter.flask import SlackRequestHandler
17 |
18 | flask_app = Flask(__name__)
19 | handler = SlackRequestHandler(app)
20 |
21 |
22 | @flask_app.route("/slack/events", methods=["POST"])
23 | def slack_events():
24 | return handler.handle(request)
25 |
26 |
27 | # heroku login
28 | # heroku create
29 | # git remote add heroku https://git.heroku.com/xxx.git
30 |
31 | # export SLACK_BOT_TOKEN=xxx
32 | # export SLACK_SIGNING_SECRET=xxx
33 | # heroku config:set SLACK_BOT_TOKEN=$SLACK_BOT_TOKEN
34 | # heroku config:set SLACK_SIGNING_SECRET=$SLACK_SIGNING_SECRET
35 | # git checkout -b main
36 | # git add .
37 | # git commit -m'Initial commit for my awesome Slack app'
38 | # git push heroku main
39 |
--------------------------------------------------------------------------------
/examples/heroku/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt
2 | Flask>=1.1
3 | gunicorn>=20
--------------------------------------------------------------------------------
/examples/oauth_sqlite3_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 | from slack_bolt.oauth import OAuthFlow
7 |
8 | app = App(
9 | oauth_flow=OAuthFlow.sqlite3(
10 | database="./slackapp.db",
11 | token_rotation_expiration_minutes=60 * 24, # for testing
12 | )
13 | )
14 |
15 |
16 | @app.event("app_mention")
17 | def handle_app_mentions(body, say, logger):
18 | logger.info(body)
19 | say("What's up?")
20 |
21 |
22 | @app.command("/token-rotation-modal")
23 | def handle_some_command(ack, body, logger):
24 | ack()
25 | logger.info(body)
26 |
27 |
28 | if __name__ == "__main__":
29 | app.start(3000)
30 |
31 | # pip install slack_bolt
32 | # export SLACK_SIGNING_SECRET=***
33 | # export SLACK_BOT_TOKEN=xoxb-***
34 | # export SLACK_CLIENT_ID=111.111
35 | # export SLACK_CLIENT_SECRET=***
36 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
37 | # python oauth_app.py
38 |
--------------------------------------------------------------------------------
/examples/oauth_sqlite3_app_bot_only.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 | from slack_bolt.oauth import OAuthFlow
7 |
8 | app = App(
9 | oauth_flow=OAuthFlow.sqlite3(
10 | database="./slackapp.db",
11 | token_rotation_expiration_minutes=60 * 24, # for testing
12 | ),
13 | installation_store_bot_only=True,
14 | )
15 |
16 |
17 | @app.event("app_mention")
18 | def handle_app_mentions(body, say, logger):
19 | logger.info(body)
20 | say("What's up?")
21 |
22 |
23 | @app.command("/token-rotation-modal")
24 | def handle_some_command(ack, body, logger):
25 | ack()
26 | logger.info(body)
27 |
28 |
29 | if __name__ == "__main__":
30 | app.start(3000)
31 |
32 | # pip install slack_bolt
33 | # export SLACK_SIGNING_SECRET=***
34 | # export SLACK_BOT_TOKEN=xoxb-***
35 | # export SLACK_CLIENT_ID=111.111
36 | # export SLACK_CLIENT_SECRET=***
37 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
38 | # python oauth_app.py
39 |
--------------------------------------------------------------------------------
/examples/pyramid/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from slack_bolt import App
3 | from slack_bolt.adapter.pyramid.handler import SlackRequestHandler
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = App()
7 |
8 |
9 | @app.event("app_mention")
10 | def event_test(body, say, logger):
11 | logger.info(body)
12 | say("What's up?")
13 |
14 |
15 | handler = SlackRequestHandler(app)
16 |
17 | if __name__ == "__main__":
18 | from wsgiref.simple_server import make_server
19 | from pyramid.config import Configurator
20 |
21 | with Configurator() as config:
22 | config.add_route("slack_events", "/slack/events")
23 | config.add_view(handler.handle, route_name="slack_events", request_method="POST")
24 | pyramid_app = config.make_wsgi_app()
25 | server = make_server("0.0.0.0", 3000, pyramid_app)
26 | server.serve_forever()
27 |
28 | # export SLACK_SIGNING_SECRET=***
29 | # export SLACK_BOT_TOKEN=xoxb-***
30 | # python app.py
31 |
--------------------------------------------------------------------------------
/examples/pyramid/requirements.txt:
--------------------------------------------------------------------------------
1 | pyramid>=1,<2
2 |
--------------------------------------------------------------------------------
/examples/sanic/async_app.py:
--------------------------------------------------------------------------------
1 | import os
2 | from slack_bolt.async_app import AsyncApp
3 | from slack_bolt.adapter.sanic import AsyncSlackRequestHandler
4 |
5 | app = AsyncApp()
6 | app_handler = AsyncSlackRequestHandler(app)
7 |
8 |
9 | @app.event("app_mention")
10 | async def handle_app_mentions(body, say, logger):
11 | logger.info(body)
12 | await say("What's up?")
13 |
14 |
15 | from sanic import Sanic
16 | from sanic.request import Request
17 |
18 | api = Sanic(name="awesome-slack-app")
19 |
20 |
21 | @api.post("/slack/events")
22 | async def endpoint(req: Request):
23 | return await app_handler.handle(req)
24 |
25 |
26 | if __name__ == "__main__":
27 | api.run(host="0.0.0.0", port=int(os.environ.get("PORT", 3000)))
28 |
29 |
30 | # pip install -r requirements.txt
31 | # export SLACK_SIGNING_SECRET=***
32 | # export SLACK_BOT_TOKEN=xoxb-***
33 | # uvicorn async_app:api --reload --port 3000 --log-level debug
34 |
--------------------------------------------------------------------------------
/examples/sanic/requirements.txt:
--------------------------------------------------------------------------------
1 | sanic>=20,<21
2 | uvicorn<1
3 |
--------------------------------------------------------------------------------
/examples/socket_mode_proxy.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | import os
6 |
7 | from slack_sdk import WebClient
8 | from slack_bolt import App
9 | from slack_bolt.adapter.socket_mode import SocketModeHandler
10 |
11 | # pip3 install proxy.py
12 | # proxy --port 9000 --log-level d
13 | proxy_url = "http://localhost:9000"
14 |
15 | # Install the Slack app and get xoxb- token in advance
16 | app = App(client=WebClient(token=os.environ["SLACK_BOT_TOKEN"], proxy=proxy_url))
17 |
18 |
19 | @app.event("app_mention")
20 | def event_test(event, say):
21 | say(f"Hi there, <@{event['user']}>!")
22 |
23 |
24 | if __name__ == "__main__":
25 | # export SLACK_APP_TOKEN=xapp-***
26 | # export SLACK_BOT_TOKEN=xoxb-***
27 | SocketModeHandler(
28 | app=app,
29 | app_token=os.environ["SLACK_APP_TOKEN"],
30 | proxy=proxy_url,
31 | ).start()
32 |
--------------------------------------------------------------------------------
/examples/sqlalchemy/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_bolt>=0.6
2 | Flask>1
3 | SQLAlchemy
4 |
--------------------------------------------------------------------------------
/examples/sqlalchemy/requirements_async.txt:
--------------------------------------------------------------------------------
1 | slack_bolt>=0.6
2 | sanic>=20,<21
3 | uvicorn<1
4 | databases[sqlite]
--------------------------------------------------------------------------------
/examples/starlette/app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.starlette import SlackRequestHandler
3 |
4 | app = App()
5 |
6 |
7 | @app.event("app_mention")
8 | def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | say("What's up?")
11 |
12 |
13 | app_handler = SlackRequestHandler(app)
14 |
15 | from starlette.applications import Starlette
16 | from starlette.requests import Request
17 | from starlette.routing import Route
18 |
19 |
20 | async def endpoint(req: Request):
21 | return await app_handler.handle(req)
22 |
23 |
24 | api = Starlette(debug=True, routes=[Route("/slack/events", endpoint=endpoint, methods=["POST"])])
25 |
26 | # pip install -r requirements.txt
27 | # export SLACK_SIGNING_SECRET=***
28 | # export SLACK_BOT_TOKEN=xoxb-***
29 | # uvicorn app:api --reload --port 3000 --log-level debug
30 |
--------------------------------------------------------------------------------
/examples/starlette/async_app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.async_app import AsyncApp
2 | from slack_bolt.adapter.starlette.async_handler import AsyncSlackRequestHandler
3 |
4 | app = AsyncApp()
5 |
6 |
7 | @app.event("app_mention")
8 | async def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | await say("What's up?")
11 |
12 |
13 | app_handler = AsyncSlackRequestHandler(app)
14 |
15 | from starlette.applications import Starlette
16 | from starlette.requests import Request
17 | from starlette.routing import Route
18 |
19 |
20 | async def endpoint(req: Request):
21 | return await app_handler.handle(req)
22 |
23 |
24 | api = Starlette(debug=True, routes=[Route("/slack/events", endpoint=endpoint, methods=["POST"])])
25 |
26 | # pip install -r requirements.txt
27 | # export SLACK_SIGNING_SECRET=***
28 | # export SLACK_BOT_TOKEN=xoxb-***
29 | # uvicorn async_app:api --reload --port 3000 --log-level debug
30 |
--------------------------------------------------------------------------------
/examples/starlette/requirements.txt:
--------------------------------------------------------------------------------
1 | starlette>=0.13,<1
2 | uvicorn<1
3 |
--------------------------------------------------------------------------------
/examples/tornado/app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt import App
6 | from slack_bolt.adapter.tornado import SlackEventsHandler
7 |
8 | app = App()
9 |
10 |
11 | @app.middleware # or app.use(log_request)
12 | def log_request(logger, body, next_):
13 | logger.debug(body)
14 | next_()
15 |
16 |
17 | @app.event("app_mention")
18 | def event_test(body, say, logger):
19 | logger.info(body)
20 | say("What's up?")
21 |
22 |
23 | from tornado.web import Application
24 | from tornado.ioloop import IOLoop
25 |
26 | api = Application([("/slack/events", SlackEventsHandler, dict(app=app))])
27 |
28 | if __name__ == "__main__":
29 | api.listen(3000)
30 | IOLoop.current().start()
31 |
32 | # pip install -r requirements.txt
33 | # export SLACK_SIGNING_SECRET=***
34 | # export SLACK_BOT_TOKEN=xoxb-***
35 | # python app.py
36 |
--------------------------------------------------------------------------------
/examples/tornado/async_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logging.basicConfig(level=logging.DEBUG)
4 |
5 | from slack_bolt.async_app import AsyncApp
6 | from slack_bolt.adapter.tornado.async_handler import AsyncSlackEventsHandler
7 |
8 | app = AsyncApp()
9 |
10 |
11 | @app.middleware # or app.use(log_request)
12 | async def log_request(logger, body, next_):
13 | logger.debug(body)
14 | await next_()
15 |
16 |
17 | @app.event("app_mention")
18 | async def event_test(body, say, logger):
19 | logger.info(body)
20 | await say("What's up?")
21 |
22 |
23 | from tornado.web import Application
24 | from tornado.ioloop import IOLoop
25 |
26 | api = Application([("/slack/events", AsyncSlackEventsHandler, dict(app=app))])
27 |
28 | if __name__ == "__main__":
29 | api.listen(3000)
30 | IOLoop.current().start()
31 |
32 | # pip install -r requirements.txt
33 | # export SLACK_SIGNING_SECRET=***
34 | # export SLACK_BOT_TOKEN=xoxb-***
35 | # python async_app.py
36 |
--------------------------------------------------------------------------------
/examples/tornado/oauth_app.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from slack_bolt import App
3 | from slack_bolt.adapter.tornado import SlackEventsHandler, SlackOAuthHandler
4 |
5 | logging.basicConfig(level=logging.DEBUG)
6 | app = App()
7 |
8 |
9 | @app.middleware # or app.use(log_request)
10 | def log_request(logger, body, next):
11 | logger.debug(body)
12 | next()
13 |
14 |
15 | @app.event("app_mention")
16 | def event_test(body, say, logger):
17 | logger.info(body)
18 | say("What's up?")
19 |
20 |
21 | from tornado.web import Application
22 | from tornado.ioloop import IOLoop
23 |
24 | api = Application(
25 | [
26 | ("/slack/events", SlackEventsHandler, dict(app=app)),
27 | ("/slack/install", SlackOAuthHandler, dict(app=app)),
28 | ("/slack/oauth_redirect", SlackOAuthHandler, dict(app=app)),
29 | ]
30 | )
31 |
32 | if __name__ == "__main__":
33 | api.listen(3000)
34 | IOLoop.current().start()
35 |
36 | # pip install -r requirements.txt
37 |
38 | # # -- OAuth flow -- #
39 | # export SLACK_SIGNING_SECRET=***
40 | # export SLACK_BOT_TOKEN=xoxb-***
41 | # export SLACK_CLIENT_ID=111.111
42 | # export SLACK_CLIENT_SECRET=***
43 | # export SLACK_SCOPES=app_mentions:read,chat:write
44 |
45 | # python oauth_app.py
46 |
--------------------------------------------------------------------------------
/examples/tornado/requirements.txt:
--------------------------------------------------------------------------------
1 | tornado>=6,<7
--------------------------------------------------------------------------------
/examples/wsgi/app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.wsgi import SlackRequestHandler
3 |
4 | app = App()
5 |
6 |
7 | @app.event("app_mention")
8 | def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | say("What's up?")
11 |
12 |
13 | api = SlackRequestHandler(app)
14 |
15 | # pip install -r requirements.txt
16 | # export SLACK_SIGNING_SECRET=***
17 | # export SLACK_BOT_TOKEN=xoxb-***
18 | # gunicorn app:api -b 0.0.0.0:3000 --log-level debug
19 | # ngrok http 3000
20 |
--------------------------------------------------------------------------------
/examples/wsgi/oauth_app.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import App
2 | from slack_bolt.adapter.wsgi import SlackRequestHandler
3 |
4 | app = App()
5 |
6 |
7 | @app.event("app_mention")
8 | def handle_app_mentions(body, say, logger):
9 | logger.info(body)
10 | say("What's up?")
11 |
12 |
13 | api = SlackRequestHandler(app)
14 |
15 | # pip install -r requirements.txt
16 |
17 | # # -- OAuth flow -- #
18 | # export SLACK_SIGNING_SECRET=***
19 | # export SLACK_CLIENT_ID=111.111
20 | # export SLACK_CLIENT_SECRET=***
21 | # export SLACK_SCOPES=app_mentions:read,channels:history,im:history,chat:write
22 |
23 | # gunicorn oauth_app:api -b 0.0.0.0:3000 --log-level debug
24 |
--------------------------------------------------------------------------------
/examples/wsgi/requirements.txt:
--------------------------------------------------------------------------------
1 | gunicorn<23
2 |
--------------------------------------------------------------------------------
/logs/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/logs/.gitkeep
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | slack_sdk>=3.35.0,<4
2 |
--------------------------------------------------------------------------------
/requirements/adapter.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/adapter.txt
2 | # NOTE: any of async ones requires pip install -r requirements/async.txt too
3 | # used only under slack_bolt/adapter
4 | boto3<=2
5 | bottle>=0.12,<1
6 | chalice<=1.27.3; python_version=="3.6"
7 | chalice>=1.28,<2; python_version>"3.6"
8 | CherryPy>=18,<19
9 | Django>=3,<6
10 | falcon>=2,<5; python_version<"3.11"
11 | falcon>=3.1.1,<5; python_version>="3.11"
12 | fastapi>=0.70.0,<1
13 | Flask>=1,<4
14 | Werkzeug>=2,<4
15 | pyramid>=1,<3
16 | sanic>=20,<21; python_version=="3.6"
17 | sanic>=21,<24; python_version>"3.6" and python_version<="3.8"
18 | sanic>=21,<26; python_version>"3.8"
19 | starlette>=0.19.1,<1
20 | tornado>=6,<7
21 | uvicorn<1 # The oldest version can vary among Python runtime versions
22 | gunicorn>=20,<24
23 | websocket_client>=1.2.3,<2 # Socket Mode 3rd party implementation
24 |
--------------------------------------------------------------------------------
/requirements/adapter_testing.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/adapter_testing.txt
2 | moto>=3,<6 # For AWS tests
3 | docker>=5,<8 # Used by moto
4 | boddle>=0.2,<0.3 # For Bottle app tests
5 | sanic-testing>=0.7; python_version>"3.6"
6 |
--------------------------------------------------------------------------------
/requirements/async.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/async.txt
2 | aiohttp>=3,<4
3 | websockets<16
4 |
--------------------------------------------------------------------------------
/requirements/testing.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/testing.txt
2 | -r testing_without_asyncio.txt
3 | -r async.txt
4 | pytest-asyncio<1;
5 |
--------------------------------------------------------------------------------
/requirements/testing_without_asyncio.txt:
--------------------------------------------------------------------------------
1 | # pip install -r requirements/testing_without_asyncio.txt
2 | pytest>=6.2.5,<8.4 # https://github.com/tornadoweb/tornado/issues/3375
3 | pytest-cov>=3,<7
4 |
--------------------------------------------------------------------------------
/requirements/tools.txt:
--------------------------------------------------------------------------------
1 | mypy==1.16.0
2 | flake8==7.2.0
3 | black==24.8.0 # Until we drop Python 3.6 support, we have to stay with this version
4 |
--------------------------------------------------------------------------------
/scripts/build_pypi_package.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_dir=`dirname $0`
4 | cd ${script_dir}/..
5 | rm -rf ./slack_bolt.egg-info
6 |
7 | pip install -U pip && \
8 | pip install twine build && \
9 | rm -rf dist/ build/ slack_bolt.egg-info/ && \
10 | python -m build --sdist --wheel && \
11 | twine check dist/*
12 |
--------------------------------------------------------------------------------
/scripts/deploy_to_pypi_org.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_dir=`dirname $0`
4 | cd ${script_dir}/..
5 | rm -rf ./slack_bolt.egg-info
6 |
7 | pip install -U pip && \
8 | pip install twine build && \
9 | rm -rf dist/ build/ slack_bolt.egg-info/ && \
10 | python -m build --sdist --wheel && \
11 | twine check dist/* && \
12 | twine upload dist/*
13 |
--------------------------------------------------------------------------------
/scripts/deploy_to_test_pypi_org.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | script_dir=`dirname $0`
4 | cd ${script_dir}/..
5 | rm -rf ./slack_bolt.egg-info
6 |
7 | pip install -U pip && \
8 | pip install twine build && \
9 | rm -rf dist/ build/ slack_bolt.egg-info/ && \
10 | python -m build --sdist --wheel && \
11 | twine check dist/* && \
12 | twine upload --repository testpypi dist/*
--------------------------------------------------------------------------------
/scripts/generate_api_docs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Generate API documents from the latest source code
3 |
4 | script_dir=`dirname $0`
5 | cd ${script_dir}/..
6 |
7 | pip install -U pdoc3
8 | rm -rf docs/static/api-docs
9 | pdoc slack_bolt --html -o docs/static/api-docs
10 | open docs/static/api-docs/slack_bolt/index.html
11 |
--------------------------------------------------------------------------------
/scripts/run_flake8.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ./scripts/run_flake8.sh
3 |
4 | script_dir=$(dirname $0)
5 | cd ${script_dir}/.. && \
6 | pip install -r requirements/tools.txt && \
7 | flake8 slack_bolt/ && flake8 examples/
8 |
--------------------------------------------------------------------------------
/scripts/run_mypy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # ./scripts/run_mypy.sh
3 |
4 | script_dir=$(dirname $0)
5 | cd ${script_dir}/.. && \
6 | pip install .
7 | pip install -r requirements/async.txt && \
8 | pip install -r requirements/adapter.txt && \
9 | pip install -r requirements/tools.txt && \
10 | mypy --config-file pyproject.toml
11 |
--------------------------------------------------------------------------------
/scripts/run_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Run all the tests or a single test
3 | # all: ./scripts/run_tests.sh
4 | # single: ./scripts/run_tests.sh tests/scenario_tests/test_app.py
5 |
6 | script_dir=`dirname $0`
7 | cd ${script_dir}/..
8 |
9 | test_target="$1"
10 | python_version=`python --version | awk '{print $2}'`
11 |
12 | if [[ $test_target != "" ]]
13 | then
14 | black slack_bolt/ tests/ && \
15 | pytest -vv $1
16 | else
17 | black slack_bolt/ tests/ && pytest
18 | fi
19 |
--------------------------------------------------------------------------------
/scripts/uninstall_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | pip uninstall -y slack-bolt && \
4 | pip freeze | grep -v "^-e" | xargs pip uninstall -y
5 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | ; Legacy package configuration, prefer pyproject.toml over setup.cfg
2 | [metadata]
3 | url=https://github.com/slackapi/bolt-python
4 | author=Slack Technologies, LLC
5 | author_email=opensource@slack.com
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/__init__.py:
--------------------------------------------------------------------------------
1 | """Adapter modules for running Bolt apps along with Web frameworks or Socket Mode.
2 | """
3 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/asgi/__init__.py:
--------------------------------------------------------------------------------
1 | from .builtin import SlackRequestHandler
2 |
3 | __all__ = ["SlackRequestHandler"]
4 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/asgi/async_handler.py:
--------------------------------------------------------------------------------
1 | from .aiohttp import AsyncSlackRequestHandler
2 |
3 | __all__ = ["AsyncSlackRequestHandler"]
4 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/asgi/http_request.py:
--------------------------------------------------------------------------------
1 | from typing import Callable, Dict, Iterable, Sequence, Tuple, Union
2 |
3 | from .utils import scope_type, ENCODING
4 |
5 |
6 | class AsgiHttpRequest:
7 | __slots__ = ("receive", "query_string", "raw_headers")
8 |
9 | def __init__(self, scope: scope_type, receive: Callable):
10 | self.receive = receive
11 | self.query_string = str(scope["query_string"], ENCODING) # type: ignore[arg-type]
12 | self.raw_headers: Iterable[Tuple[bytes, bytes]] = scope["headers"] # type: ignore[assignment]
13 |
14 | def get_headers(self) -> Dict[str, Union[str, Sequence[str]]]:
15 | return {str(header[0], ENCODING): str(header[1], (ENCODING)) for header in self.raw_headers}
16 |
17 | async def get_raw_body(self) -> str:
18 | chunks = bytearray()
19 | while True:
20 | chunk: Dict[str, Union[str, bytes]] = await self.receive()
21 |
22 | if chunk["type"] != "http.request":
23 | raise Exception("Body chunks could not be received from asgi server")
24 |
25 | chunks.extend(chunk.get("body", b"")) # type: ignore[arg-type]
26 | if not chunk.get("more_body", False):
27 | break
28 | return bytes(chunks).decode(ENCODING)
29 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/asgi/http_response.py:
--------------------------------------------------------------------------------
1 | from typing import Iterable, Sequence, Tuple, Dict, Union, List
2 |
3 | from .utils import ENCODING
4 |
5 |
6 | class AsgiHttpResponse:
7 | __slots__ = ("status", "raw_headers", "body")
8 |
9 | def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""):
10 | self.status: int = status
11 | self.raw_headers: List[Tuple[bytes, bytes]] = [
12 | (bytes(key, ENCODING), bytes(value[0], ENCODING)) for key, value in headers.items()
13 | ]
14 | self.raw_headers.append((b"content-length", bytes(str(len(body)), ENCODING)))
15 | self.body: bytes = bytes(body, ENCODING)
16 |
17 | def get_response_start(self) -> Dict[str, Union[str, int, Iterable[Tuple[bytes, bytes]]]]:
18 | return {
19 | "type": "http.response.start",
20 | "status": self.status,
21 | "headers": self.raw_headers,
22 | }
23 |
24 | def get_response_body(self) -> Dict[str, Union[str, bytes, bool]]:
25 | return {
26 | "type": "http.response.body",
27 | "body": self.body,
28 | "more_body": False,
29 | }
30 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/asgi/utils.py:
--------------------------------------------------------------------------------
1 | from typing import Iterable, Tuple, Union, Dict
2 |
3 | ENCODING = "utf-8" # should always be utf-8
4 |
5 | scope_value_type = Union[str, bytes, Iterable[Tuple[bytes, bytes]]]
6 |
7 | scope_type = Dict[str, scope_value_type]
8 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/aws_lambda/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/aws_lambda/internals.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Optional, Sequence
2 |
3 |
4 | def _first_value(query: Dict[str, Sequence[str]], name: str) -> Optional[str]:
5 | if query:
6 | values = query.get(name, [])
7 | if values and len(values) > 0:
8 | return values[0]
9 | return None
10 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/aws_lambda/lazy_listener_runner.py:
--------------------------------------------------------------------------------
1 | import json
2 | from logging import Logger
3 | from typing import Callable, Optional, Any
4 |
5 | import boto3 # type: ignore[import-untyped]
6 |
7 | from slack_bolt import BoltRequest
8 | from slack_bolt.lazy_listener import LazyListenerRunner
9 |
10 |
11 | class LambdaLazyListenerRunner(LazyListenerRunner):
12 | def __init__(self, logger: Logger, lambda_client: Optional[Any] = None):
13 | self.lambda_client = lambda_client
14 | self.logger = logger
15 |
16 | def start(self, function: Callable[..., None], request: BoltRequest) -> None:
17 | if self.lambda_client is None:
18 | self.lambda_client = boto3.client("lambda")
19 |
20 | event: dict = request.context["lambda_request"]
21 | headers = event["headers"]
22 | headers["x-slack-bolt-lazy-only"] = "1" # not an array
23 | headers["x-slack-bolt-lazy-function-name"] = request.lazy_function_name # not an array
24 | event["method"] = "NONE"
25 | invocation = self.lambda_client.invoke(
26 | FunctionName=request.context["aws_lambda_invoked_function_arn"],
27 | InvocationType="Event",
28 | Payload=json.dumps(event),
29 | )
30 | self.logger.info(invocation)
31 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/aws_lambda/local_lambda_client.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from chalice.app import Chalice
4 | from chalice.config import Config
5 | from chalice.test import BaseClient, LambdaContext, InvokeResponse
6 |
7 |
8 | class LocalLambdaClient(BaseClient):
9 | """Lambda client implementing `invoke` for use when running with Chalice CLI."""
10 |
11 | def __init__(self, app: Chalice, config: Config) -> None:
12 | self._app = app
13 | self._config = config if config else Config()
14 |
15 | def invoke(
16 | self,
17 | FunctionName: str,
18 | InvocationType: str = "Event",
19 | Payload: str = "{}",
20 | ) -> InvokeResponse:
21 | scoped = self._config.scope(self._config.chalice_stage, FunctionName)
22 | lambda_context = LambdaContext(FunctionName, memory_size=scoped.lambda_memory_size)
23 |
24 | with self._patched_env_vars(scoped.environment_variables):
25 | response = self._app(json.loads(Payload), lambda_context)
26 | return InvokeResponse(payload=response)
27 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/bottle/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/cherrypy/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/django/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/falcon/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .resource import SlackAppResource
3 |
4 | __all__ = [
5 | "SlackAppResource",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/fastapi/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from ..starlette.handler import SlackRequestHandler
3 |
4 | __all__ = [
5 | "SlackRequestHandler",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/fastapi/async_handler.py:
--------------------------------------------------------------------------------
1 | from ..starlette.async_handler import AsyncSlackRequestHandler
2 |
3 | __all__ = [
4 | "AsyncSlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/flask/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/google_cloud_functions/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/pyramid/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = [
4 | "SlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/sanic/__init__.py:
--------------------------------------------------------------------------------
1 | from .async_handler import AsyncSlackRequestHandler
2 |
3 | __all__ = [
4 | "AsyncSlackRequestHandler",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/socket_mode/__init__.py:
--------------------------------------------------------------------------------
1 | """Socket Mode adapter package provides the following implementations. If you don't have strong reasons to use 3rd party library based adapters, we recommend using the built-in client based one.
2 |
3 | * `slack_bolt.adapter.socket_mode.builtin`
4 | * `slack_bolt.adapter.socket_mode.websocket_client`
5 | * `slack_bolt.adapter.socket_mode.aiohttp`
6 | * `slack_bolt.adapter.socket_mode.websockets`
7 | """ # noqa: E501
8 |
9 | # Don't add async module imports here
10 | from .builtin import SocketModeHandler
11 |
12 | __all__ = [
13 | "SocketModeHandler",
14 | ]
15 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/socket_mode/async_handler.py:
--------------------------------------------------------------------------------
1 | """Default implementation is the aiohttp-based one."""
2 |
3 | from .aiohttp import AsyncSocketModeHandler
4 |
5 | __all__ = [
6 | "AsyncSocketModeHandler",
7 | ]
8 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/starlette/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .handler import SlackRequestHandler
3 |
4 | __all__ = [
5 | "SlackRequestHandler",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/tornado/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .handler import SlackEventsHandler, SlackOAuthHandler
3 |
4 | __all__ = [
5 | "SlackEventsHandler",
6 | "SlackOAuthHandler",
7 | ]
8 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/wsgi/__init__.py:
--------------------------------------------------------------------------------
1 | from .handler import SlackRequestHandler
2 |
3 | __all__ = ["SlackRequestHandler"]
4 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/wsgi/http_response.py:
--------------------------------------------------------------------------------
1 | from http import HTTPStatus
2 | from typing import Dict, Iterable, List, Sequence, Tuple
3 |
4 | from .internals import ENCODING
5 |
6 |
7 | class WsgiHttpResponse:
8 | """This Class uses the PEP 3333 standard to adapt bolt response information
9 | for the WSGI web server running the application
10 |
11 | PEP 3333: https://peps.python.org/pep-3333/
12 | """
13 |
14 | __slots__ = ("status", "_headers", "_body")
15 |
16 | def __init__(self, status: int, headers: Dict[str, Sequence[str]] = {}, body: str = ""):
17 | _status = HTTPStatus(status)
18 | self.status = f"{_status.value} {_status.phrase}"
19 | self._headers = headers
20 | self._body = bytes(body, ENCODING)
21 |
22 | def get_headers(self) -> List[Tuple[str, str]]:
23 | headers: List[Tuple[str, str]] = []
24 | for key, value in self._headers.items():
25 | if key.lower() == "content-length":
26 | continue
27 | headers.append((key, value[0]))
28 |
29 | headers.append(("content-length", str(len(self._body))))
30 | return headers
31 |
32 | def get_body(self) -> Iterable[bytes]:
33 | return [self._body]
34 |
--------------------------------------------------------------------------------
/slack_bolt/adapter/wsgi/internals.py:
--------------------------------------------------------------------------------
1 | ENCODING = "utf-8" # The content encoding for Slack requests/responses is always utf-8
2 |
--------------------------------------------------------------------------------
/slack_bolt/app/__init__.py:
--------------------------------------------------------------------------------
1 | # flake8: noqa
2 | """Application interface in Bolt.
3 |
4 | For most use cases, we recommend using `slack_bolt.app.app`.
5 | If you already have knowledge about asyncio and prefer the programming model,
6 | you can use `slack_bolt.app.async_app` for building async apps.\
7 | """
8 |
9 | # Don't add async module imports here
10 | from .app import App
11 |
12 | __all__ = [
13 | "App",
14 | ]
15 |
--------------------------------------------------------------------------------
/slack_bolt/authorization/__init__.py:
--------------------------------------------------------------------------------
1 | """Authorization is the process of determining which Slack credentials should be available
2 | while processing an incoming Slack event.
3 |
4 | Refer to https://slack.dev/bolt-python/concepts#authorization for details.
5 | """
6 |
7 | from .authorize_result import AuthorizeResult
8 |
9 | __all__ = [
10 | "AuthorizeResult",
11 | ]
12 |
--------------------------------------------------------------------------------
/slack_bolt/authorization/async_authorize_args.py:
--------------------------------------------------------------------------------
1 | from logging import Logger
2 | from typing import Optional
3 |
4 | from slack_sdk.web.async_client import AsyncWebClient
5 |
6 | from slack_bolt.context.async_context import AsyncBoltContext
7 |
8 |
9 | class AsyncAuthorizeArgs:
10 | context: AsyncBoltContext
11 | logger: Logger
12 | client: AsyncWebClient
13 | enterprise_id: Optional[str]
14 | team_id: Optional[str]
15 | user_id: Optional[str]
16 |
17 | def __init__(
18 | self,
19 | *,
20 | context: AsyncBoltContext,
21 | enterprise_id: Optional[str],
22 | team_id: Optional[str], # can be None for org-wide installed apps
23 | user_id: Optional[str],
24 | ):
25 | """The full list of the arguments passed to `authorize` function.
26 |
27 | Args:
28 | context: The request context
29 | enterprise_id: The Organization ID (Enterprise Grid)
30 | team_id: The workspace ID
31 | user_id: The request user ID
32 | """
33 | self.context = context
34 | self.logger = context.logger
35 | self.client = context.client
36 | self.enterprise_id = enterprise_id
37 | self.team_id = team_id
38 | self.user_id = user_id
39 |
--------------------------------------------------------------------------------
/slack_bolt/authorization/authorize_args.py:
--------------------------------------------------------------------------------
1 | from logging import Logger
2 | from typing import Optional
3 |
4 | from slack_sdk.web import WebClient
5 |
6 | from slack_bolt.context.context import BoltContext
7 |
8 |
9 | class AuthorizeArgs:
10 | context: BoltContext
11 | logger: Logger
12 | client: WebClient
13 | enterprise_id: Optional[str]
14 | team_id: Optional[str]
15 | user_id: Optional[str]
16 |
17 | def __init__(
18 | self,
19 | *,
20 | context: BoltContext,
21 | enterprise_id: Optional[str],
22 | team_id: Optional[str], # can be None for org-wide installed apps
23 | user_id: Optional[str],
24 | ):
25 | """The full list of the arguments passed to `authorize` function.
26 |
27 | Args:
28 | context: The request context
29 | enterprise_id: The Organization ID (Enterprise Grid)
30 | team_id: The workspace ID
31 | user_id: The request user ID
32 | """
33 | self.context = context
34 | self.logger = context.logger
35 | self.client = context.client
36 | self.enterprise_id = enterprise_id
37 | self.team_id = team_id
38 | self.user_id = user_id
39 |
--------------------------------------------------------------------------------
/slack_bolt/context/__init__.py:
--------------------------------------------------------------------------------
1 | """All listeners have access to a context dictionary, which can be used to enrich events with additional information.
2 | Bolt automatically attaches information that is included in the incoming event,
3 | like `user_id`, `team_id`, `channel_id`, and `enterprise_id`.
4 |
5 | Refer to https://slack.dev/bolt-python/concepts#context for details.
6 | """
7 |
8 | # Don't add async module imports here
9 | from .context import BoltContext
10 |
11 | __all__ = [
12 | "BoltContext",
13 | ]
14 |
--------------------------------------------------------------------------------
/slack_bolt/context/ack/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .ack import Ack
3 |
4 | __all__ = [
5 | "Ack",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/assistant/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 |
--------------------------------------------------------------------------------
/slack_bolt/context/assistant/internals.py:
--------------------------------------------------------------------------------
1 | def has_channel_id_and_thread_ts(payload: dict) -> bool:
2 | """Verifies if the given payload has both channel_id and thread_ts under assistant_thread property.
3 | This data pattern is available for assistant_* events.
4 | """
5 | return (
6 | payload.get("assistant_thread") is not None
7 | and payload["assistant_thread"].get("channel_id") is not None
8 | and payload["assistant_thread"].get("thread_ts") is not None
9 | )
10 |
--------------------------------------------------------------------------------
/slack_bolt/context/assistant/thread_context/__init__.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 |
4 | class AssistantThreadContext(dict):
5 | enterprise_id: Optional[str]
6 | team_id: Optional[str]
7 | channel_id: str
8 |
9 | def __init__(self, payload: dict):
10 | dict.__init__(self, **payload)
11 | self.enterprise_id = payload.get("enterprise_id")
12 | self.team_id = payload.get("team_id")
13 | self.channel_id = payload["channel_id"]
14 |
--------------------------------------------------------------------------------
/slack_bolt/context/assistant/thread_context_store/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 |
--------------------------------------------------------------------------------
/slack_bolt/context/assistant/thread_context_store/async_store.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Optional
2 |
3 | from slack_bolt.context.assistant.thread_context import AssistantThreadContext
4 |
5 |
6 | class AsyncAssistantThreadContextStore:
7 | async def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
8 | raise NotImplementedError()
9 |
10 | async def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
11 | raise NotImplementedError()
12 |
--------------------------------------------------------------------------------
/slack_bolt/context/assistant/thread_context_store/store.py:
--------------------------------------------------------------------------------
1 | from typing import Dict, Optional
2 |
3 | from slack_bolt.context.assistant.thread_context import AssistantThreadContext
4 |
5 |
6 | class AssistantThreadContextStore:
7 | def save(self, *, channel_id: str, thread_ts: str, context: Dict[str, str]) -> None:
8 | raise NotImplementedError()
9 |
10 | def find(self, *, channel_id: str, thread_ts: str) -> Optional[AssistantThreadContext]:
11 | raise NotImplementedError()
12 |
--------------------------------------------------------------------------------
/slack_bolt/context/complete/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .complete import Complete
3 |
4 | __all__ = [
5 | "Complete",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/complete/complete.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Dict, Optional
2 |
3 | from slack_sdk import WebClient
4 | from slack_sdk.web import SlackResponse
5 |
6 |
7 | class Complete:
8 | client: WebClient
9 | function_execution_id: Optional[str]
10 |
11 | def __init__(
12 | self,
13 | client: WebClient,
14 | function_execution_id: Optional[str],
15 | ):
16 | self.client = client
17 | self.function_execution_id = function_execution_id
18 |
19 | def __call__(self, outputs: Optional[Dict[str, Any]] = None) -> SlackResponse:
20 | """Signal the successful completion of the custom function.
21 |
22 | Kwargs:
23 | outputs: Json serializable object containing the output values
24 |
25 | Returns:
26 | SlackResponse: The response object returned from slack
27 |
28 | Raises:
29 | ValueError: If this function cannot be used.
30 | """
31 | if self.function_execution_id is None:
32 | raise ValueError("complete is unsupported here as there is no function_execution_id")
33 |
34 | return self.client.functions_completeSuccess(function_execution_id=self.function_execution_id, outputs=outputs or {})
35 |
--------------------------------------------------------------------------------
/slack_bolt/context/fail/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .fail import Fail
3 |
4 | __all__ = [
5 | "Fail",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/fail/async_fail.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from slack_sdk.web.async_client import AsyncWebClient
4 | from slack_sdk.web.async_slack_response import AsyncSlackResponse
5 |
6 |
7 | class AsyncFail:
8 | client: AsyncWebClient
9 | function_execution_id: Optional[str]
10 |
11 | def __init__(
12 | self,
13 | client: AsyncWebClient,
14 | function_execution_id: Optional[str],
15 | ):
16 | self.client = client
17 | self.function_execution_id = function_execution_id
18 |
19 | async def __call__(self, error: str) -> AsyncSlackResponse:
20 | """Signal that the custom function failed to complete.
21 |
22 | Kwargs:
23 | error: Error message to return to slack
24 |
25 | Returns:
26 | SlackResponse: The response object returned from slack
27 |
28 | Raises:
29 | ValueError: If this function cannot be used.
30 | """
31 | if self.function_execution_id is None:
32 | raise ValueError("fail is unsupported here as there is no function_execution_id")
33 |
34 | return await self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error)
35 |
--------------------------------------------------------------------------------
/slack_bolt/context/fail/fail.py:
--------------------------------------------------------------------------------
1 | from typing import Optional
2 |
3 | from slack_sdk import WebClient
4 | from slack_sdk.web import SlackResponse
5 |
6 |
7 | class Fail:
8 | client: WebClient
9 | function_execution_id: Optional[str]
10 |
11 | def __init__(
12 | self,
13 | client: WebClient,
14 | function_execution_id: Optional[str],
15 | ):
16 | self.client = client
17 | self.function_execution_id = function_execution_id
18 |
19 | def __call__(self, error: str) -> SlackResponse:
20 | """Signal that the custom function failed to complete.
21 |
22 | Kwargs:
23 | error: Error message to return to slack
24 |
25 | Returns:
26 | SlackResponse: The response object returned from slack
27 |
28 | Raises:
29 | ValueError: If this function cannot be used.
30 | """
31 | if self.function_execution_id is None:
32 | raise ValueError("fail is unsupported here as there is no function_execution_id")
33 |
34 | return self.client.functions_completeError(function_execution_id=self.function_execution_id, error=error)
35 |
--------------------------------------------------------------------------------
/slack_bolt/context/get_thread_context/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .get_thread_context import GetThreadContext
3 |
4 | __all__ = [
5 | "GetThreadContext",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/respond/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .respond import Respond
3 |
4 | __all__ = [
5 | "Respond",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/save_thread_context/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .save_thread_context import SaveThreadContext
3 |
4 | __all__ = [
5 | "SaveThreadContext",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/save_thread_context/async_save_thread_context.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | from slack_bolt.context.assistant.thread_context_store.async_store import AsyncAssistantThreadContextStore
4 |
5 |
6 | class AsyncSaveThreadContext:
7 | thread_context_store: AsyncAssistantThreadContextStore
8 | channel_id: str
9 | thread_ts: str
10 |
11 | def __init__(
12 | self,
13 | thread_context_store: AsyncAssistantThreadContextStore,
14 | channel_id: str,
15 | thread_ts: str,
16 | ):
17 | self.thread_context_store = thread_context_store
18 | self.channel_id = channel_id
19 | self.thread_ts = thread_ts
20 |
21 | async def __call__(self, new_context: Dict[str, str]) -> None:
22 | await self.thread_context_store.save(
23 | channel_id=self.channel_id,
24 | thread_ts=self.thread_ts,
25 | context=new_context,
26 | )
27 |
--------------------------------------------------------------------------------
/slack_bolt/context/save_thread_context/save_thread_context.py:
--------------------------------------------------------------------------------
1 | from typing import Dict
2 |
3 | from slack_bolt.context.assistant.thread_context_store.store import AssistantThreadContextStore
4 |
5 |
6 | class SaveThreadContext:
7 | thread_context_store: AssistantThreadContextStore
8 | channel_id: str
9 | thread_ts: str
10 |
11 | def __init__(
12 | self,
13 | thread_context_store: AssistantThreadContextStore,
14 | channel_id: str,
15 | thread_ts: str,
16 | ):
17 | self.thread_context_store = thread_context_store
18 | self.channel_id = channel_id
19 | self.thread_ts = thread_ts
20 |
21 | def __call__(self, new_context: Dict[str, str]) -> None:
22 | self.thread_context_store.save(
23 | channel_id=self.channel_id,
24 | thread_ts=self.thread_ts,
25 | context=new_context,
26 | )
27 |
--------------------------------------------------------------------------------
/slack_bolt/context/say/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .say import Say
3 |
4 | __all__ = [
5 | "Say",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/say/internals.py:
--------------------------------------------------------------------------------
1 | from typing import Optional, Any
2 |
3 |
4 | def _can_say(self: Any, channel: Optional[str]) -> bool:
5 | return hasattr(self, "client") and self.client is not None and (channel or self.channel) is not None
6 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_status/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .set_status import SetStatus
3 |
4 | __all__ = [
5 | "SetStatus",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_status/async_set_status.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.web.async_client import AsyncWebClient
2 | from slack_sdk.web.async_slack_response import AsyncSlackResponse
3 |
4 |
5 | class AsyncSetStatus:
6 | client: AsyncWebClient
7 | channel_id: str
8 | thread_ts: str
9 |
10 | def __init__(
11 | self,
12 | client: AsyncWebClient,
13 | channel_id: str,
14 | thread_ts: str,
15 | ):
16 | self.client = client
17 | self.channel_id = channel_id
18 | self.thread_ts = thread_ts
19 |
20 | async def __call__(self, status: str) -> AsyncSlackResponse:
21 | return await self.client.assistant_threads_setStatus(
22 | status=status,
23 | channel_id=self.channel_id,
24 | thread_ts=self.thread_ts,
25 | )
26 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_status/set_status.py:
--------------------------------------------------------------------------------
1 | from slack_sdk import WebClient
2 | from slack_sdk.web import SlackResponse
3 |
4 |
5 | class SetStatus:
6 | client: WebClient
7 | channel_id: str
8 | thread_ts: str
9 |
10 | def __init__(
11 | self,
12 | client: WebClient,
13 | channel_id: str,
14 | thread_ts: str,
15 | ):
16 | self.client = client
17 | self.channel_id = channel_id
18 | self.thread_ts = thread_ts
19 |
20 | def __call__(self, status: str) -> SlackResponse:
21 | return self.client.assistant_threads_setStatus(
22 | status=status,
23 | channel_id=self.channel_id,
24 | thread_ts=self.thread_ts,
25 | )
26 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_suggested_prompts/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .set_suggested_prompts import SetSuggestedPrompts
3 |
4 | __all__ = [
5 | "SetSuggestedPrompts",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_suggested_prompts/set_suggested_prompts.py:
--------------------------------------------------------------------------------
1 | from typing import List, Dict, Union, Optional
2 |
3 | from slack_sdk import WebClient
4 | from slack_sdk.web import SlackResponse
5 |
6 |
7 | class SetSuggestedPrompts:
8 | client: WebClient
9 | channel_id: str
10 | thread_ts: str
11 |
12 | def __init__(
13 | self,
14 | client: WebClient,
15 | channel_id: str,
16 | thread_ts: str,
17 | ):
18 | self.client = client
19 | self.channel_id = channel_id
20 | self.thread_ts = thread_ts
21 |
22 | def __call__(
23 | self,
24 | prompts: List[Union[str, Dict[str, str]]],
25 | title: Optional[str] = None,
26 | ) -> SlackResponse:
27 | prompts_arg: List[Dict[str, str]] = []
28 | for prompt in prompts:
29 | if isinstance(prompt, str):
30 | prompts_arg.append({"title": prompt, "message": prompt})
31 | else:
32 | prompts_arg.append(prompt)
33 |
34 | return self.client.assistant_threads_setSuggestedPrompts(
35 | channel_id=self.channel_id,
36 | thread_ts=self.thread_ts,
37 | prompts=prompts_arg,
38 | title=title,
39 | )
40 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_title/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .set_title import SetTitle
3 |
4 | __all__ = [
5 | "SetTitle",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_title/async_set_title.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.web.async_client import AsyncWebClient
2 | from slack_sdk.web.async_slack_response import AsyncSlackResponse
3 |
4 |
5 | class AsyncSetTitle:
6 | client: AsyncWebClient
7 | channel_id: str
8 | thread_ts: str
9 |
10 | def __init__(
11 | self,
12 | client: AsyncWebClient,
13 | channel_id: str,
14 | thread_ts: str,
15 | ):
16 | self.client = client
17 | self.channel_id = channel_id
18 | self.thread_ts = thread_ts
19 |
20 | async def __call__(self, title: str) -> AsyncSlackResponse:
21 | return await self.client.assistant_threads_setTitle(
22 | title=title,
23 | channel_id=self.channel_id,
24 | thread_ts=self.thread_ts,
25 | )
26 |
--------------------------------------------------------------------------------
/slack_bolt/context/set_title/set_title.py:
--------------------------------------------------------------------------------
1 | from slack_sdk import WebClient
2 | from slack_sdk.web import SlackResponse
3 |
4 |
5 | class SetTitle:
6 | client: WebClient
7 | channel_id: str
8 | thread_ts: str
9 |
10 | def __init__(
11 | self,
12 | client: WebClient,
13 | channel_id: str,
14 | thread_ts: str,
15 | ):
16 | self.client = client
17 | self.channel_id = channel_id
18 | self.thread_ts = thread_ts
19 |
20 | def __call__(self, title: str) -> SlackResponse:
21 | return self.client.assistant_threads_setTitle(
22 | title=title,
23 | channel_id=self.channel_id,
24 | thread_ts=self.thread_ts,
25 | )
26 |
--------------------------------------------------------------------------------
/slack_bolt/error/__init__.py:
--------------------------------------------------------------------------------
1 | """Bolt specific error types."""
2 |
3 | from typing import Optional, Union
4 |
5 |
6 | class BoltError(Exception):
7 | """General class in a Bolt app"""
8 |
9 |
10 | class BoltUnhandledRequestError(BoltError):
11 | request: "BoltRequest" # type: ignore[name-defined]
12 | body: dict
13 | current_response: Optional["BoltResponse"] # type: ignore[name-defined]
14 | last_global_middleware_name: Optional[str]
15 |
16 | def __init__(
17 | self,
18 | *,
19 | request: Union["BoltRequest", "AsyncBoltRequest"], # type: ignore[name-defined]
20 | current_response: Optional["BoltResponse"], # type: ignore[name-defined]
21 | last_global_middleware_name: Optional[str] = None,
22 | ):
23 | self.request = request
24 | self.body = request.body if request is not None else {}
25 | self.current_response = current_response
26 | self.last_global_middleware_name = last_global_middleware_name
27 |
28 | def __str__(self) -> str:
29 | return "unhandled request error"
30 |
--------------------------------------------------------------------------------
/slack_bolt/kwargs_injection/__init__.py:
--------------------------------------------------------------------------------
1 | """For middleware/listener arguments, Bolt does flexible data injection in accordance with their names.
2 |
3 | To learn the available arguments, check `slack_bolt.kwargs_injection.args`'s API document.
4 | For steps from apps, checking `slack_bolt.workflows.step.utilities` as well should be helpful.
5 | """
6 |
7 | # Don't add async module imports here
8 | from .args import Args
9 | from .utils import build_required_kwargs
10 |
11 | __all__ = [
12 | "Args",
13 | "build_required_kwargs",
14 | ]
15 |
--------------------------------------------------------------------------------
/slack_bolt/lazy_listener/__init__.py:
--------------------------------------------------------------------------------
1 | """Lazy listener runner is a beta feature for the apps running on Function-as-a-Service platforms.
2 |
3 | def respond_to_slack_within_3_seconds(body, ack):
4 | text = body.get("text")
5 | if text is None or len(text) == 0:
6 | ack(f":x: Usage: /start-process (description here)")
7 | else:
8 | ack(f"Accepted! (task: {body['text']})")
9 |
10 | import time
11 | def run_long_process(respond, body):
12 | time.sleep(5) # longer than 3 seconds
13 | respond(f"Completed! (task: {body['text']})")
14 |
15 | app.command("/start-process")(
16 | # ack() is still called within 3 seconds
17 | ack=respond_to_slack_within_3_seconds,
18 | # Lazy function is responsible for processing the event
19 | lazy=[run_long_process]
20 | )
21 |
22 | Refer to https://slack.dev/bolt-python/concepts#lazy-listeners for more details.
23 | """
24 |
25 | # Don't add async module imports here
26 | from .runner import LazyListenerRunner
27 | from .thread_runner import ThreadLazyListenerRunner
28 |
29 | __all__ = [
30 | "LazyListenerRunner",
31 | "ThreadLazyListenerRunner",
32 | ]
33 |
--------------------------------------------------------------------------------
/slack_bolt/lazy_listener/async_internals.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 | from logging import Logger
3 | from typing import Callable, Awaitable
4 |
5 | from slack_bolt.kwargs_injection.async_utils import build_async_required_kwargs
6 | from slack_bolt.request.async_request import AsyncBoltRequest
7 | from slack_bolt.util.utils import get_arg_names_of_callable
8 |
9 |
10 | async def to_runnable_function(
11 | internal_func: Callable[..., Awaitable[None]],
12 | logger: Logger,
13 | request: AsyncBoltRequest,
14 | ):
15 | arg_names = get_arg_names_of_callable(internal_func)
16 |
17 | @wraps(internal_func)
18 | async def request_wired_wrapper() -> None:
19 | try:
20 | await internal_func(
21 | **build_async_required_kwargs(
22 | logger=logger,
23 | required_arg_names=arg_names,
24 | request=request,
25 | response=None,
26 | this_func=internal_func,
27 | )
28 | )
29 | except Exception as e:
30 | logger.error(f"Failed to run an internal function ({e})")
31 |
32 | return await request_wired_wrapper()
33 |
--------------------------------------------------------------------------------
/slack_bolt/lazy_listener/asyncio_runner.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from logging import Logger
3 | from typing import Callable, Awaitable
4 |
5 | from slack_bolt.lazy_listener.async_internals import to_runnable_function
6 | from slack_bolt.lazy_listener.async_runner import AsyncLazyListenerRunner
7 | from slack_bolt.request.async_request import AsyncBoltRequest
8 |
9 |
10 | class AsyncioLazyListenerRunner(AsyncLazyListenerRunner):
11 | logger: Logger
12 |
13 | def __init__(
14 | self,
15 | logger: Logger,
16 | ):
17 | self.logger = logger
18 |
19 | def start(self, function: Callable[..., Awaitable[None]], request: AsyncBoltRequest) -> None:
20 | asyncio.ensure_future(
21 | to_runnable_function(
22 | internal_func=function,
23 | logger=self.logger,
24 | request=request,
25 | )
26 | )
27 |
--------------------------------------------------------------------------------
/slack_bolt/lazy_listener/internals.py:
--------------------------------------------------------------------------------
1 | from functools import wraps
2 | from logging import Logger
3 | from typing import Callable
4 |
5 | from slack_bolt.kwargs_injection import build_required_kwargs
6 | from slack_bolt.request import BoltRequest
7 | from slack_bolt.util.utils import get_arg_names_of_callable
8 |
9 |
10 | def build_runnable_function(
11 | func: Callable[..., None],
12 | logger: Logger,
13 | request: BoltRequest,
14 | ) -> Callable[[], None]:
15 | arg_names = get_arg_names_of_callable(func)
16 |
17 | @wraps(func)
18 | def request_wired_func_wrapper() -> None:
19 | try:
20 | func(
21 | **build_required_kwargs(
22 | logger=logger,
23 | required_arg_names=arg_names,
24 | request=request,
25 | response=None,
26 | this_func=func,
27 | )
28 | )
29 | except Exception as e:
30 | logger.error(f"Failed to run an internal function ({e})")
31 |
32 | return request_wired_func_wrapper
33 |
--------------------------------------------------------------------------------
/slack_bolt/lazy_listener/runner.py:
--------------------------------------------------------------------------------
1 | from abc import abstractmethod, ABCMeta
2 | from logging import Logger
3 | from typing import Callable
4 |
5 | from slack_bolt.lazy_listener.internals import build_runnable_function
6 | from slack_bolt.request import BoltRequest
7 |
8 |
9 | class LazyListenerRunner(metaclass=ABCMeta):
10 | logger: Logger
11 |
12 | @abstractmethod
13 | def start(self, function: Callable[..., None], request: BoltRequest) -> None:
14 | """Starts a new lazy listener execution.
15 |
16 | Args:
17 | function: The function to run.
18 | request: The request to pass to the function. The object must be thread-safe.
19 | """
20 | raise NotImplementedError()
21 |
22 | def run(self, function: Callable[..., None], request: BoltRequest) -> None:
23 | """Synchronously runs the function with a given request data.
24 |
25 | Args:
26 | function: The function to run.
27 | request: The request to pass to the function. The object must be thread-safe.
28 | """
29 | build_runnable_function(
30 | func=function,
31 | logger=self.logger,
32 | request=request,
33 | )()
34 |
--------------------------------------------------------------------------------
/slack_bolt/lazy_listener/thread_runner.py:
--------------------------------------------------------------------------------
1 | from concurrent.futures import Executor
2 | from logging import Logger
3 | from typing import Callable
4 |
5 | from slack_bolt.lazy_listener.internals import build_runnable_function
6 | from slack_bolt.lazy_listener.runner import LazyListenerRunner
7 | from slack_bolt.request import BoltRequest
8 |
9 |
10 | class ThreadLazyListenerRunner(LazyListenerRunner):
11 | logger: Logger
12 |
13 | def __init__(
14 | self,
15 | logger: Logger,
16 | executor: Executor,
17 | ):
18 | self.logger = logger
19 | self.executor = executor
20 |
21 | def start(self, function: Callable[..., None], request: BoltRequest) -> None:
22 | self.executor.submit(
23 | build_runnable_function(
24 | func=function,
25 | logger=self.logger,
26 | request=request,
27 | )
28 | )
29 |
--------------------------------------------------------------------------------
/slack_bolt/listener/__init__.py:
--------------------------------------------------------------------------------
1 | """Listeners process an incoming request from Slack if the request's type or data structure matches
2 | the predefined conditions of the listener. Typically, a listener acknowledge requests from Slack,
3 | process the request data, and may send response back to Slack.
4 | """
5 |
6 | # Don't add async module imports here
7 | from .custom_listener import CustomListener
8 | from .listener import Listener
9 |
10 | builtin_listener_classes = [
11 | CustomListener,
12 | ]
13 | for cls in builtin_listener_classes:
14 | Listener.register(cls)
15 |
16 | __all__ = [
17 | "CustomListener",
18 | "Listener",
19 | "builtin_listener_classes",
20 | ]
21 |
--------------------------------------------------------------------------------
/slack_bolt/listener_matcher/__init__.py:
--------------------------------------------------------------------------------
1 | """A listener matcher is a simplified version of listener middleware.
2 | A listener matcher function returns bool value instead of `next()` method invocation inside.
3 | This interface enables developers to utilize simple predicate functions for additional listener conditions.
4 | """
5 |
6 | # Don't add async module imports here
7 | from .custom_listener_matcher import CustomListenerMatcher
8 | from .listener_matcher import ListenerMatcher
9 |
10 | builtin_listener_matcher_classes = [
11 | CustomListenerMatcher,
12 | ]
13 | for cls in builtin_listener_matcher_classes:
14 | ListenerMatcher.register(cls)
15 |
16 | __all__ = [
17 | "CustomListenerMatcher",
18 | "ListenerMatcher",
19 | "builtin_listener_matcher_classes",
20 | ]
21 |
--------------------------------------------------------------------------------
/slack_bolt/listener_matcher/async_builtins.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.request.async_request import AsyncBoltRequest
2 | from slack_bolt.response import BoltResponse
3 | from .async_listener_matcher import AsyncListenerMatcher
4 | from .builtins import BuiltinListenerMatcher
5 | from ..kwargs_injection.async_utils import build_async_required_kwargs
6 |
7 |
8 | class AsyncBuiltinListenerMatcher(BuiltinListenerMatcher, AsyncListenerMatcher):
9 | async def async_matches(self, req: AsyncBoltRequest, resp: BoltResponse) -> bool:
10 | return await self.func( # type: ignore[misc]
11 | **build_async_required_kwargs(
12 | logger=self.logger,
13 | required_arg_names=self.arg_names,
14 | request=req,
15 | response=resp,
16 | this_func=self.func,
17 | )
18 | )
19 |
--------------------------------------------------------------------------------
/slack_bolt/listener_matcher/listener_matcher.py:
--------------------------------------------------------------------------------
1 | from abc import abstractmethod, ABCMeta
2 |
3 | from slack_bolt.request import BoltRequest
4 | from slack_bolt.response import BoltResponse
5 |
6 |
7 | class ListenerMatcher(metaclass=ABCMeta):
8 | @abstractmethod
9 | def matches(self, req: BoltRequest, resp: BoltResponse) -> bool:
10 | """Matches against the request and returns True if matched.
11 |
12 | Args:
13 | req: The request
14 | resp: The response
15 |
16 | Returns:
17 | True if matched.
18 | """
19 | raise NotImplementedError()
20 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/assistant/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .assistant import Assistant
3 |
4 | __all__ = [
5 | "Assistant",
6 | ]
7 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/async_builtins.py:
--------------------------------------------------------------------------------
1 | from .ignoring_self_events.async_ignoring_self_events import (
2 | AsyncIgnoringSelfEvents,
3 | )
4 | from .request_verification.async_request_verification import (
5 | AsyncRequestVerification,
6 | )
7 | from .ssl_check.async_ssl_check import AsyncSslCheck
8 | from .url_verification.async_url_verification import AsyncUrlVerification
9 | from .message_listener_matches.async_message_listener_matches import (
10 | AsyncMessageListenerMatches,
11 | )
12 | from .attaching_function_token.async_attaching_function_token import AsyncAttachingFunctionToken
13 |
14 | __all__ = [
15 | "AsyncIgnoringSelfEvents",
16 | "AsyncRequestVerification",
17 | "AsyncSslCheck",
18 | "AsyncUrlVerification",
19 | "AsyncMessageListenerMatches",
20 | "AsyncAttachingFunctionToken",
21 | ]
22 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/attaching_function_token/__init__.py:
--------------------------------------------------------------------------------
1 | from .attaching_function_token import AttachingFunctionToken
2 |
3 | __all__ = [
4 | "AttachingFunctionToken",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/attaching_function_token/async_attaching_function_token.py:
--------------------------------------------------------------------------------
1 | from typing import Callable, Awaitable
2 |
3 | from slack_bolt.request.async_request import AsyncBoltRequest
4 | from slack_bolt.response import BoltResponse
5 | from slack_bolt.middleware.async_middleware import AsyncMiddleware
6 |
7 |
8 | class AsyncAttachingFunctionToken(AsyncMiddleware):
9 | async def async_process(
10 | self,
11 | *,
12 | req: AsyncBoltRequest,
13 | resp: BoltResponse,
14 | # This method is not supposed to be invoked by bolt-python users
15 | next: Callable[[], Awaitable[BoltResponse]],
16 | ) -> BoltResponse:
17 | if req.context.function_bot_access_token is not None:
18 | req.context.client.token = req.context.function_bot_access_token
19 |
20 | return await next()
21 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/attaching_function_token/attaching_function_token.py:
--------------------------------------------------------------------------------
1 | from typing import Callable
2 |
3 | from slack_bolt.request import BoltRequest
4 | from slack_bolt.response import BoltResponse
5 | from slack_bolt.middleware.middleware import Middleware
6 |
7 |
8 | class AttachingFunctionToken(Middleware):
9 | def process(
10 | self,
11 | *,
12 | req: BoltRequest,
13 | resp: BoltResponse,
14 | # This method is not supposed to be invoked by bolt-python users
15 | next: Callable[[], BoltResponse],
16 | ) -> BoltResponse:
17 | if req.context.function_bot_access_token is not None:
18 | req.context.client.token = req.context.function_bot_access_token
19 |
20 | return next()
21 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/authorization/__init__.py:
--------------------------------------------------------------------------------
1 | # Don't add async module imports here
2 | from .authorization import Authorization
3 | from .multi_teams_authorization import MultiTeamsAuthorization
4 | from .single_team_authorization import SingleTeamAuthorization
5 |
6 | __all__ = [
7 | "Authorization",
8 | "MultiTeamsAuthorization",
9 | "SingleTeamAuthorization",
10 | ]
11 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/authorization/async_authorization.py:
--------------------------------------------------------------------------------
1 | from abc import ABC
2 |
3 | from slack_bolt.middleware.async_middleware import AsyncMiddleware
4 |
5 |
6 | class AsyncAuthorization(AsyncMiddleware, ABC):
7 | pass
8 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/authorization/async_internals.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.request.async_request import AsyncBoltRequest
2 | from slack_bolt.response import BoltResponse
3 |
4 |
5 | def _is_url_verification(req: AsyncBoltRequest) -> bool:
6 | return req is not None and req.body is not None and req.body.get("type") == "url_verification"
7 |
8 |
9 | def _is_ssl_check(req: AsyncBoltRequest) -> bool:
10 | return req is not None and req.body is not None and req.body.get("type") == "ssl_check"
11 |
12 |
13 | def _is_no_auth_required(req: AsyncBoltRequest) -> bool:
14 | return _is_url_verification(req) or _is_ssl_check(req)
15 |
16 |
17 | def _build_user_facing_error_response(message: str) -> BoltResponse:
18 | # show an ephemeral message to the end-user
19 | return BoltResponse(
20 | status=200,
21 | body=message,
22 | )
23 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/authorization/authorization.py:
--------------------------------------------------------------------------------
1 | from ..middleware import Middleware
2 |
3 |
4 | class Authorization(Middleware):
5 | pass
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/ignoring_self_events/__init__.py:
--------------------------------------------------------------------------------
1 | from .ignoring_self_events import IgnoringSelfEvents
2 |
3 | __all__ = [
4 | "IgnoringSelfEvents",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/message_listener_matches/__init__.py:
--------------------------------------------------------------------------------
1 | from .message_listener_matches import MessageListenerMatches
2 |
3 | __all__ = [
4 | "MessageListenerMatches",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/request_verification/__init__.py:
--------------------------------------------------------------------------------
1 | from .request_verification import RequestVerification
2 |
3 | __all__ = [
4 | "RequestVerification",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/ssl_check/__init__.py:
--------------------------------------------------------------------------------
1 | from .ssl_check import SslCheck
2 |
3 | __all__ = [
4 | "SslCheck",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/ssl_check/async_ssl_check.py:
--------------------------------------------------------------------------------
1 | from typing import Callable, Awaitable
2 |
3 | from .ssl_check import SslCheck
4 | from slack_bolt.middleware.async_middleware import AsyncMiddleware
5 | from slack_bolt.request.async_request import AsyncBoltRequest
6 | from slack_bolt.response import BoltResponse
7 |
8 |
9 | class AsyncSslCheck(SslCheck, AsyncMiddleware):
10 | async def async_process(
11 | self,
12 | *,
13 | req: AsyncBoltRequest,
14 | resp: BoltResponse,
15 | # As this method is not supposed to be invoked by bolt-python users,
16 | # the naming conflict with the built-in one affects
17 | # only the internals of this method
18 | next: Callable[[], Awaitable[BoltResponse]],
19 | ) -> BoltResponse:
20 | if self._is_ssl_check_request(req.body):
21 | if self._verify_token_if_needed(req.body):
22 | return self._build_error_response()
23 | return self._build_success_response()
24 | else:
25 | return await next()
26 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/url_verification/__init__.py:
--------------------------------------------------------------------------------
1 | from .url_verification import UrlVerification
2 |
3 | __all__ = [
4 | "UrlVerification",
5 | ]
6 |
--------------------------------------------------------------------------------
/slack_bolt/middleware/url_verification/async_url_verification.py:
--------------------------------------------------------------------------------
1 | from logging import Logger
2 | from typing import Callable, Awaitable, Optional
3 |
4 | from slack_bolt.logger import get_bolt_logger
5 | from .url_verification import UrlVerification
6 | from slack_bolt.middleware.async_middleware import AsyncMiddleware
7 | from slack_bolt.request.async_request import AsyncBoltRequest
8 | from slack_bolt.response import BoltResponse
9 |
10 |
11 | class AsyncUrlVerification(UrlVerification, AsyncMiddleware):
12 | def __init__(self, base_logger: Optional[Logger] = None):
13 | self.logger = get_bolt_logger(AsyncUrlVerification, base_logger=base_logger)
14 |
15 | async def async_process(
16 | self,
17 | *,
18 | req: AsyncBoltRequest,
19 | resp: BoltResponse,
20 | next: Callable[[], Awaitable[BoltResponse]],
21 | ) -> BoltResponse:
22 | if self._is_url_verification_request(req.body):
23 | return self._build_success_response(req.body)
24 | else:
25 | return await next()
26 |
--------------------------------------------------------------------------------
/slack_bolt/oauth/__init__.py:
--------------------------------------------------------------------------------
1 | """Slack OAuth flow support for building an app that is installable in any workspaces.
2 |
3 | Refer to https://slack.dev/bolt-python/concepts#authenticating-oauth for details.
4 | """
5 |
6 | # Don't add async module imports here
7 | from .oauth_flow import OAuthFlow
8 |
9 | __all__ = [
10 | "OAuthFlow",
11 | ]
12 |
--------------------------------------------------------------------------------
/slack_bolt/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/slack_bolt/py.typed
--------------------------------------------------------------------------------
/slack_bolt/request/__init__.py:
--------------------------------------------------------------------------------
1 | """Incoming request from Slack through either HTTP request or Socket Mode connection.
2 |
3 | Refer to https://api.slack.com/apis/connections for the two types of connections.
4 | This interface encapsulates the difference between the two.
5 | """
6 |
7 | # Don't add async module imports here
8 | from .request import BoltRequest
9 |
10 | __all__ = [
11 | "BoltRequest",
12 | ]
13 |
--------------------------------------------------------------------------------
/slack_bolt/response/__init__.py:
--------------------------------------------------------------------------------
1 | """This interface represents Bolt's synchronous response to Slack.
2 |
3 | In Socket Mode, the response data can be transformed to a WebSocket message. In the HTTP endpoint mode,
4 | the response data becomes an HTTP response data.
5 |
6 | Refer to https://api.slack.com/apis/connections for the two types of connections.
7 | """
8 |
9 | from .response import BoltResponse
10 |
11 | __all__ = [
12 | "BoltResponse",
13 | ]
14 |
--------------------------------------------------------------------------------
/slack_bolt/util/__init__.py:
--------------------------------------------------------------------------------
1 | """Internal utilities for the Bolt framework."""
2 |
--------------------------------------------------------------------------------
/slack_bolt/util/async_utils.py:
--------------------------------------------------------------------------------
1 | from logging import Logger
2 | from typing import Optional
3 |
4 | from slack_sdk.web.async_client import AsyncWebClient
5 |
6 | from slack_bolt.version import __version__ as bolt_version
7 |
8 |
9 | def create_async_web_client(token: Optional[str] = None, logger: Optional[Logger] = None) -> AsyncWebClient:
10 | return AsyncWebClient(
11 | token=token,
12 | logger=logger,
13 | user_agent_prefix=f"Bolt-Async/{bolt_version}",
14 | )
15 |
--------------------------------------------------------------------------------
/slack_bolt/version.py:
--------------------------------------------------------------------------------
1 | """Check the latest version at https://pypi.org/project/slack-bolt/"""
2 |
3 | __version__ = "1.23.0"
4 |
--------------------------------------------------------------------------------
/slack_bolt/workflows/__init__.py:
--------------------------------------------------------------------------------
1 | """Steps from apps enables developers to build their own steps.
2 |
3 | Check the following API documents first:
4 |
5 | * `slack_bolt.workflows.step.step`
6 | * `slack_bolt.workflows.step.utilities`
7 | * `slack_bolt.workflows.step.async_step` (if you use asyncio-based `AsyncApp`)
8 |
9 | Refer to https://api.slack.com/workflows/steps for details.
10 | """
11 |
--------------------------------------------------------------------------------
/slack_bolt/workflows/step/__init__.py:
--------------------------------------------------------------------------------
1 | from .step import WorkflowStep
2 | from .step_middleware import WorkflowStepMiddleware
3 | from .utilities.complete import Complete
4 | from .utilities.configure import Configure
5 | from .utilities.update import Update
6 | from .utilities.fail import Fail
7 |
8 | __all__ = [
9 | "WorkflowStep",
10 | "WorkflowStepMiddleware",
11 | "Complete",
12 | "Configure",
13 | "Update",
14 | "Fail",
15 | ]
16 |
--------------------------------------------------------------------------------
/slack_bolt/workflows/step/internals.py:
--------------------------------------------------------------------------------
1 | def _is_used_without_argument(args):
2 | """Tests if a decorator invocation is without () or (args).
3 |
4 | Args:
5 | args: arguments
6 |
7 | Returns:
8 | True if it's an invocation without args
9 | """
10 | return len(args) == 1
11 |
--------------------------------------------------------------------------------
/slack_bolt/workflows/step/utilities/__init__.py:
--------------------------------------------------------------------------------
1 | """Utilities specific to steps from apps.
2 |
3 | In steps from apps listeners, you can use a few specific listener/middleware arguments.
4 |
5 | ### `edit` listener
6 |
7 | * `slack_bolt.workflows.step.utilities.configure` for building a modal view
8 |
9 | ### `save` listener
10 |
11 | * `slack_bolt.workflows.step.utilities.update` for updating the step metadata
12 |
13 | ### `execute` listener
14 |
15 | * `slack_bolt.workflows.step.utilities.fail` for notifying the execution failure to Slack
16 | * `slack_bolt.workflows.step.utilities.complete` for notifying the execution completion to Slack
17 |
18 | For asyncio-based apps, refer to the corresponding `async` prefixed ones.
19 | """
20 |
--------------------------------------------------------------------------------
/slack_bolt/workflows/step/utilities/async_fail.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.web.async_client import AsyncWebClient
2 |
3 |
4 | class AsyncFail:
5 | """`fail()` utility to tell Slack the execution failure of a step from app.
6 |
7 | async def execute(step, complete, fail):
8 | inputs = step["inputs"]
9 | # if something went wrong
10 | error = {"message": "Just testing step failure!"}
11 | await fail(error=error)
12 |
13 | ws = AsyncWorkflowStep(
14 | callback_id="add_task",
15 | edit=edit,
16 | save=save,
17 | execute=execute,
18 | )
19 | app.step(ws)
20 |
21 | This utility is a thin wrapper of workflows.stepFailed API method.
22 | Refer to https://api.slack.com/methods/workflows.stepFailed for details.
23 | """
24 |
25 | def __init__(self, *, client: AsyncWebClient, body: dict):
26 | self.client = client
27 | self.body = body
28 |
29 | async def __call__(
30 | self,
31 | *,
32 | error: dict,
33 | ) -> None:
34 | await self.client.workflows_stepFailed(
35 | workflow_step_execute_id=self.body["event"]["workflow_step"]["workflow_step_execute_id"],
36 | error=error,
37 | )
38 |
--------------------------------------------------------------------------------
/slack_bolt/workflows/step/utilities/fail.py:
--------------------------------------------------------------------------------
1 | from slack_sdk.web import WebClient
2 |
3 |
4 | class Fail:
5 | """`fail()` utility to tell Slack the execution failure of a step from app.
6 |
7 | def execute(step, complete, fail):
8 | inputs = step["inputs"]
9 | # if something went wrong
10 | error = {"message": "Just testing step failure!"}
11 | fail(error=error)
12 |
13 | ws = WorkflowStep(
14 | callback_id="add_task",
15 | edit=edit,
16 | save=save,
17 | execute=execute,
18 | )
19 | app.step(ws)
20 |
21 | This utility is a thin wrapper of workflows.stepFailed API method.
22 | Refer to https://api.slack.com/methods/workflows.stepFailed for details.
23 | """
24 |
25 | def __init__(self, *, client: WebClient, body: dict):
26 | self.client = client
27 | self.body = body
28 |
29 | def __call__(
30 | self,
31 | *,
32 | error: dict,
33 | ) -> None:
34 | self.client.workflows_stepFailed(
35 | workflow_step_execute_id=self.body["event"]["workflow_step"]["workflow_step_execute_id"],
36 | error=error,
37 | )
38 |
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/asgi/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/asgi/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/aws/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/aws/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/aws/test_lambda_s3_oauth_flow.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.adapter.aws_lambda.lambda_s3_oauth_flow import LambdaS3OAuthFlow
2 | from slack_bolt.oauth.oauth_settings import OAuthSettings
3 | from tests.utils import remove_os_env_temporarily, restore_os_env
4 |
5 | try:
6 | from moto import mock_aws
7 | except ImportError:
8 | from moto import mock_s3 as mock_aws
9 |
10 |
11 | class TestLambdaS3OAuthFlow:
12 | def setup_method(self):
13 | self.old_os_env = remove_os_env_temporarily()
14 |
15 | def teardown_method(self):
16 | restore_os_env(self.old_os_env)
17 |
18 | @mock_aws
19 | def test_instantiation(self):
20 | oauth_flow = LambdaS3OAuthFlow(
21 | settings=OAuthSettings(
22 | client_id="111.222",
23 | client_secret="xxx",
24 | scopes=["chat:write"],
25 | ),
26 | installation_bucket_name="dummy-installation",
27 | oauth_state_bucket_name="dummy-state",
28 | )
29 | assert oauth_flow is not None
30 | assert oauth_flow.client is not None
31 | assert oauth_flow.logger is not None
32 |
--------------------------------------------------------------------------------
/tests/adapter_tests/bottle/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/bottle/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/bottle/test_bottle_oauth.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.adapter.bottle import SlackRequestHandler
2 | from slack_bolt.app import App
3 | from slack_bolt.oauth.oauth_settings import OAuthSettings
4 |
5 | signing_secret = "secret"
6 | app = App(
7 | signing_secret=signing_secret,
8 | oauth_settings=OAuthSettings(
9 | client_id="111.111",
10 | client_secret="xxx",
11 | scopes=["chat:write", "commands"],
12 | ),
13 | )
14 | handler = SlackRequestHandler(app)
15 |
16 | from bottle import get, request, response
17 | from boddle import boddle
18 |
19 |
20 | @get("/slack/install")
21 | def install():
22 | return handler.handle(request, response)
23 |
24 |
25 | class TestBottle:
26 | def test_oauth(self):
27 | with boddle(method="GET", path="/slack/install"):
28 | response_body = install()
29 | assert response.status_code == 200
30 | assert response.headers.get("content-type") == "text/html; charset=utf-8"
31 | assert "https://slack.com/oauth/v2/authorize?state=" in response_body
32 |
--------------------------------------------------------------------------------
/tests/adapter_tests/cherrypy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/cherrypy/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/django/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/django/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/django/test_django_settings.py:
--------------------------------------------------------------------------------
1 | SECRET_KEY = "XXX"
2 | DATABASES = {
3 | "default": {
4 | "ENGINE": "django.db.backends.sqlite3",
5 | "NAME": "logs/db.sqlite3",
6 | }
7 | }
8 | # Django 4 warning: The default value of USE_TZ will change from False to True in Django 5.0.
9 | # Set USE_TZ to False in your project settings if you want to keep the current default behavior.
10 | USE_TZ = False
11 |
--------------------------------------------------------------------------------
/tests/adapter_tests/falcon/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/falcon/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/flask/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/flask/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/google_cloud_functions/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/google_cloud_functions/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/pyramid/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/pyramid/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/socket_mode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/socket_mode/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/starlette/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/starlette/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/tornado/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/tornado/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests/wsgi/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests/wsgi/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests_async/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests_async/__init__.py
--------------------------------------------------------------------------------
/tests/adapter_tests_async/socket_mode/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/adapter_tests_async/socket_mode/__init__.py
--------------------------------------------------------------------------------
/tests/mock_web_api_server/received_requests.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from queue import Queue
3 | from typing import Optional, Union
4 |
5 |
6 | class ReceivedRequests:
7 | def __init__(self, queue: Union[Queue, asyncio.Queue]):
8 | self.queue = queue
9 | self.received_requests = {}
10 |
11 | def get(self, key: str, default: Optional[int] = None) -> Optional[int]:
12 | while not self.queue.empty():
13 | path = self.queue.get()
14 | self.received_requests[path] = self.received_requests.get(path, 0) + 1
15 | return self.received_requests.get(key, default)
16 |
17 | async def get_async(self, key: str, default: Optional[int] = None) -> Optional[int]:
18 | while not self.queue.empty():
19 | path = await self.queue.get()
20 | self.received_requests[path] = self.received_requests.get(path, 0) + 1
21 | return self.received_requests.get(key, default)
22 |
--------------------------------------------------------------------------------
/tests/scenario_tests/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/scenario_tests/__init__.py
--------------------------------------------------------------------------------
/tests/scenario_tests_async/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/scenario_tests_async/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/app/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/app/test_dev_server.py:
--------------------------------------------------------------------------------
1 | from slack_sdk import WebClient
2 |
3 | from slack_bolt.app.app import SlackAppDevelopmentServer, App
4 | from tests.mock_web_api_server import (
5 | setup_mock_web_api_server,
6 | cleanup_mock_web_api_server,
7 | )
8 | from tests.utils import remove_os_env_temporarily, restore_os_env
9 |
10 |
11 | class TestDevServer:
12 | signing_secret = "secret"
13 | valid_token = "xoxb-valid"
14 | mock_api_server_base_url = "http://localhost:8888"
15 | web_client = WebClient(
16 | token=valid_token,
17 | base_url=mock_api_server_base_url,
18 | )
19 |
20 | def setup_method(self):
21 | self.old_os_env = remove_os_env_temporarily()
22 | setup_mock_web_api_server(self)
23 |
24 | def teardown_method(self):
25 | cleanup_mock_web_api_server(self)
26 | restore_os_env(self.old_os_env)
27 |
28 | def test_instance(self):
29 | server = SlackAppDevelopmentServer(
30 | port=3001,
31 | path="/slack/events",
32 | app=App(signing_secret=self.signing_secret, client=self.web_client),
33 | )
34 | assert server is not None
35 |
--------------------------------------------------------------------------------
/tests/slack_bolt/authorization/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/authorization/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/context/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/context/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/context/test_complete.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from slack_sdk import WebClient
4 | from slack_bolt.context.complete import Complete
5 | from tests.mock_web_api_server import (
6 | setup_mock_web_api_server,
7 | cleanup_mock_web_api_server,
8 | )
9 |
10 |
11 | class TestComplete:
12 | def setup_method(self):
13 | setup_mock_web_api_server(self)
14 | valid_token = "xoxb-valid"
15 | mock_api_server_base_url = "http://localhost:8888"
16 | self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url)
17 |
18 | def teardown_method(self):
19 | cleanup_mock_web_api_server(self)
20 |
21 | def test_complete(self):
22 | complete_success = Complete(client=self.web_client, function_execution_id="fn1111")
23 |
24 | response = complete_success(outputs={"key": "value"})
25 |
26 | assert response.status_code == 200
27 |
28 | def test_complete_no_function_execution_id(self):
29 | complete = Complete(client=self.web_client, function_execution_id=None)
30 |
31 | with pytest.raises(ValueError):
32 | complete(outputs={"key": "value"})
33 |
--------------------------------------------------------------------------------
/tests/slack_bolt/context/test_fail.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from slack_sdk import WebClient
4 | from slack_bolt.context.fail import Fail
5 | from tests.mock_web_api_server import (
6 | setup_mock_web_api_server,
7 | cleanup_mock_web_api_server,
8 | )
9 |
10 |
11 | class TestFail:
12 | def setup_method(self):
13 | setup_mock_web_api_server(self)
14 | valid_token = "xoxb-valid"
15 | mock_api_server_base_url = "http://localhost:8888"
16 | self.web_client = WebClient(token=valid_token, base_url=mock_api_server_base_url)
17 |
18 | def teardown_method(self):
19 | cleanup_mock_web_api_server(self)
20 |
21 | def test_fail(self):
22 | fail = Fail(client=self.web_client, function_execution_id="fn1111")
23 |
24 | response = fail(error="something went wrong")
25 |
26 | assert response.status_code == 200
27 |
28 | def test_fail_no_function_execution_id(self):
29 | fail = Fail(client=self.web_client, function_execution_id=None)
30 |
31 | with pytest.raises(ValueError):
32 | fail(error="there was an error")
33 |
--------------------------------------------------------------------------------
/tests/slack_bolt/error/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/error/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/error/test_errors.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import BoltRequest
2 | from slack_bolt.error import BoltUnhandledRequestError
3 |
4 |
5 | class TestErrors:
6 | def setup_method(self):
7 | pass
8 |
9 | def teardown_method(self):
10 | pass
11 |
12 | def test_say(self):
13 | request = BoltRequest(body="foo=bar")
14 | exception = BoltUnhandledRequestError(
15 | request=request,
16 | current_response={},
17 | last_global_middleware_name="last_middleware",
18 | )
19 | assert str(exception) == "unhandled request error"
20 |
--------------------------------------------------------------------------------
/tests/slack_bolt/kwargs_injection/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/kwargs_injection/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/listener/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/listener/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/listener/test_listener_completion_handler.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt import BoltRequest
4 | from slack_bolt.listener.listener_completion_handler import (
5 | CustomListenerCompletionHandler,
6 | )
7 |
8 |
9 | class TestListenerCompletionHandler:
10 | def test_handler(self):
11 | result = {"called": False}
12 |
13 | def f():
14 | result["called"] = True
15 |
16 | handler = CustomListenerCompletionHandler(
17 | logger=logging.getLogger(__name__),
18 | func=f,
19 | )
20 | request = BoltRequest(
21 | body="{}",
22 | query={},
23 | headers={"content-type": ["application/json"]},
24 | context={},
25 | )
26 | handler.handle(request, None)
27 | assert result["called"] is True
28 |
--------------------------------------------------------------------------------
/tests/slack_bolt/listener/test_listener_start_handler.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | from slack_bolt import BoltRequest
4 | from slack_bolt.listener.listener_start_handler import (
5 | CustomListenerStartHandler,
6 | )
7 |
8 |
9 | class TestListenerStartHandler:
10 | def test_handler(self):
11 | result = {"called": False}
12 |
13 | def f():
14 | result["called"] = True
15 |
16 | handler = CustomListenerStartHandler(
17 | logger=logging.getLogger(__name__),
18 | func=f,
19 | )
20 | request = BoltRequest(
21 | body="{}",
22 | query={},
23 | headers={"content-type": ["application/json"]},
24 | context={},
25 | )
26 | handler.handle(request, None)
27 | assert result["called"] is True
28 |
--------------------------------------------------------------------------------
/tests/slack_bolt/listener_matcher/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/listener_matcher/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/listener_matcher/test_custom_listener_matcher.py:
--------------------------------------------------------------------------------
1 | from slack_bolt import BoltRequest, BoltResponse, CustomListenerMatcher
2 | from slack_bolt.listener_matcher import ListenerMatcher
3 |
4 |
5 | def func(body, request, response, dummy):
6 | assert body is not None
7 | assert request is not None
8 | assert response is not None
9 | assert dummy is None
10 | return body["result"]
11 |
12 |
13 | class TestCustomListenerMatcher:
14 | def setup_method(self):
15 | pass
16 |
17 | def teardown_method(self):
18 | pass
19 |
20 | def test_instantiation(self):
21 | matcher: ListenerMatcher = CustomListenerMatcher(
22 | app_name="foo",
23 | func=func,
24 | )
25 | resp = BoltResponse(status=201)
26 |
27 | req = BoltRequest(body='payload={"result":true}')
28 | result = matcher.matches(req, resp)
29 | assert result is True
30 |
31 | req = BoltRequest(body='payload={"result":false}')
32 | result = matcher.matches(req, resp)
33 | assert result is False
34 |
--------------------------------------------------------------------------------
/tests/slack_bolt/logger/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/logger/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/middleware/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/middleware/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/middleware/authorization/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/middleware/authorization/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/middleware/request_verification/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/middleware/request_verification/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/oauth/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/oauth/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/request/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/request/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/util/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/util/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/workflows/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/workflows/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/workflows/step/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt/workflows/step/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt/workflows/step/test_step.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from slack_bolt import Ack
4 | from slack_bolt.error import BoltError
5 | from slack_bolt.workflows.step import WorkflowStep
6 |
7 |
8 | class TestStep:
9 | def test_build(self):
10 | step = WorkflowStep.builder("foo")
11 | step.edit(just_ack)
12 | step.save(just_ack)
13 | step.execute(just_ack)
14 | assert step.build() is not None
15 |
16 | def test_build_errors(self):
17 | with pytest.raises(BoltError):
18 | step = WorkflowStep.builder("foo")
19 | step.save(just_ack)
20 | step.execute(just_ack)
21 | step.build()
22 | with pytest.raises(BoltError):
23 | step = WorkflowStep.builder("foo")
24 | step.edit(just_ack)
25 | step.execute(just_ack)
26 | step.build()
27 | with pytest.raises(BoltError):
28 | step = WorkflowStep.builder("foo")
29 | step.edit(just_ack)
30 | step.save(just_ack)
31 | step.build()
32 |
33 |
34 | def just_ack(ack: Ack):
35 | ack()
36 |
37 |
38 | def execute():
39 | pass
40 |
--------------------------------------------------------------------------------
/tests/slack_bolt_async/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/app/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/app/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/app/test_server.py:
--------------------------------------------------------------------------------
1 | from slack_bolt.app.async_app import AsyncApp
2 | from slack_bolt.app.async_server import AsyncSlackAppServer
3 |
4 |
5 | class TestServer:
6 | def setup_method(self):
7 | pass
8 |
9 | def teardown_method(self):
10 | pass
11 |
12 | def test_instance(self):
13 | server = AsyncSlackAppServer(
14 | port=3001,
15 | path="/slack/events",
16 | app=AsyncApp(
17 | signing_secret="valid",
18 | token="xoxb-valid",
19 | ),
20 | )
21 | assert server is not None
22 |
--------------------------------------------------------------------------------
/tests/slack_bolt_async/authorization/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/authorization/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/context/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/context/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/kwargs_injection/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/kwargs_injection/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/listener/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/listener/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/listener/test_async_listener_completion_handler.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 |
4 | from slack_bolt.request.async_request import AsyncBoltRequest
5 | from slack_bolt.listener.async_listener_completion_handler import (
6 | AsyncCustomListenerCompletionHandler,
7 | )
8 |
9 |
10 | class TestAsyncListenerCompletionHandler:
11 | @pytest.mark.asyncio
12 | async def test_handler(self):
13 | result = {"called": False}
14 |
15 | async def f():
16 | result["called"] = True
17 |
18 | handler = AsyncCustomListenerCompletionHandler(
19 | logger=logging.getLogger(__name__),
20 | func=f,
21 | )
22 | request = AsyncBoltRequest(
23 | body="{}",
24 | query={},
25 | headers={"content-type": ["application/json"]},
26 | context={},
27 | )
28 | await handler.handle(request, None)
29 | assert result["called"] is True
30 |
--------------------------------------------------------------------------------
/tests/slack_bolt_async/listener/test_async_listener_start_handler.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import pytest
3 |
4 | from slack_bolt.request.async_request import AsyncBoltRequest
5 | from slack_bolt.listener.async_listener_start_handler import (
6 | AsyncCustomListenerStartHandler,
7 | )
8 |
9 |
10 | class TestAsyncListenerStartHandler:
11 | @pytest.mark.asyncio
12 | async def test_handler(self):
13 | result = {"called": False}
14 |
15 | async def f():
16 | result["called"] = True
17 |
18 | handler = AsyncCustomListenerStartHandler(
19 | logger=logging.getLogger(__name__),
20 | func=f,
21 | )
22 | request = AsyncBoltRequest(
23 | body="{}",
24 | query={},
25 | headers={"content-type": ["application/json"]},
26 | context={},
27 | )
28 | await handler.handle(request, None)
29 | assert result["called"] is True
30 |
--------------------------------------------------------------------------------
/tests/slack_bolt_async/listener_matcher/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/listener_matcher/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/listener_matcher/test_async_custom_listener_matcher.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from slack_bolt.listener_matcher.async_listener_matcher import (
4 | AsyncCustomListenerMatcher,
5 | AsyncListenerMatcher,
6 | )
7 | from slack_bolt.request.async_request import AsyncBoltRequest
8 | from slack_bolt.response import BoltResponse
9 |
10 |
11 | async def func(body, request, response, dummy):
12 | assert body is not None
13 | assert request is not None
14 | assert response is not None
15 | assert dummy is None
16 | return body["result"]
17 |
18 |
19 | class TestAsyncCustomListenerMatcher:
20 | @pytest.mark.asyncio
21 | async def test_instantiation(self):
22 | matcher: AsyncListenerMatcher = AsyncCustomListenerMatcher(
23 | app_name="foo",
24 | func=func,
25 | )
26 | resp = BoltResponse(status=201)
27 |
28 | req = AsyncBoltRequest(body='payload={"result":true}')
29 | result = await matcher.async_matches(req, resp)
30 | assert result is True
31 |
32 | req = AsyncBoltRequest(body='payload={"result":false}')
33 | result = await matcher.async_matches(req, resp)
34 | assert result is False
35 |
--------------------------------------------------------------------------------
/tests/slack_bolt_async/logger/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/logger/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/middleware/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/middleware/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/middleware/authorization/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/middleware/authorization/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/middleware/request_verification/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/middleware/request_verification/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/oauth/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/oauth/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/request/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/request/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/request/test_async_request.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from slack_bolt.request.async_request import AsyncBoltRequest
4 |
5 |
6 | class TestAsyncRequest:
7 | @pytest.mark.asyncio
8 | async def test_all_none_values_http(self):
9 | req = AsyncBoltRequest(body=None, headers=None, query=None, context=None)
10 | assert req is not None
11 | assert req.raw_body == ""
12 | assert req.body == {}
13 |
14 | @pytest.mark.asyncio
15 | async def test_all_none_values_socket_mode(self):
16 | req = AsyncBoltRequest(body=None, headers=None, query=None, context=None, mode="socket_mode")
17 | assert req is not None
18 | assert req.raw_body == ""
19 | assert req.body == {}
20 |
--------------------------------------------------------------------------------
/tests/slack_bolt_async/workflows/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/workflows/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/workflows/step/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/slackapi/bolt-python/260e9b9c34ac930c99769077ee682dda9e0f1426/tests/slack_bolt_async/workflows/step/__init__.py
--------------------------------------------------------------------------------
/tests/slack_bolt_async/workflows/step/test_async_step.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from slack_bolt import Ack
4 | from slack_bolt.error import BoltError
5 | from slack_bolt.workflows.step.async_step import AsyncWorkflowStep
6 |
7 |
8 | class TestStep:
9 | def test_build(self):
10 | step = AsyncWorkflowStep.builder("foo")
11 | step.edit(just_ack)
12 | step.save(just_ack)
13 | step.execute(just_ack)
14 | assert step.build() is not None
15 |
16 | def test_build_errors(self):
17 | with pytest.raises(BoltError):
18 | step = AsyncWorkflowStep.builder("foo")
19 | step.save(just_ack)
20 | step.execute(just_ack)
21 | step.build()
22 | with pytest.raises(BoltError):
23 | step = AsyncWorkflowStep.builder("foo")
24 | step.edit(just_ack)
25 | step.execute(just_ack)
26 | step.build()
27 | with pytest.raises(BoltError):
28 | step = AsyncWorkflowStep.builder("foo")
29 | step.edit(just_ack)
30 | step.save(just_ack)
31 | step.build()
32 |
33 |
34 | def just_ack(ack: Ack):
35 | ack()
36 |
37 |
38 | def execute():
39 | pass
40 |
--------------------------------------------------------------------------------
/tests/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import asyncio
3 |
4 |
5 | def remove_os_env_temporarily() -> dict:
6 | old_env = os.environ.copy()
7 | os.environ.clear()
8 | for key, value in old_env.items():
9 | if key.startswith("BOLT_PYTHON_"):
10 | os.environ[key] = value
11 | return old_env
12 |
13 |
14 | def restore_os_env(old_env: dict) -> None:
15 | os.environ.update(old_env)
16 |
17 |
18 | def get_event_loop():
19 | try:
20 | return asyncio.get_event_loop()
21 | except RuntimeError as ex:
22 | if "There is no current event loop in thread" in str(ex):
23 | loop = asyncio.new_event_loop()
24 | asyncio.set_event_loop(loop)
25 | return loop
26 |
--------------------------------------------------------------------------------